home *** CD-ROM | disk | FTP | other *** search
- /* vi:set ts=8 sts=4 sw=4:
- *
- * VIM - Vi IMproved by Bram Moolenaar
- *
- * Do ":help uganda" in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- * See README.txt for an overview of the Vim source code.
- */
-
- /*
- * ex_cmds.c: some functions for command line commands
- */
-
- #include "vim.h"
- #include "version.h"
-
- #ifdef FEAT_EX_EXTRA
- static int linelen __ARGS((int *has_tab));
- #endif
- static void do_filter __ARGS((linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, int do_in, int do_out));
- #ifdef FEAT_VIMINFO
- static char_u *viminfo_filename __ARGS((char_u *));
- static void do_viminfo __ARGS((FILE *fp_in, FILE *fp_out, int want_info, int want_marks, int force_read));
- static int viminfo_encoding __ARGS((vir_T *virp));
- static int read_viminfo_up_to_marks __ARGS((vir_T *virp, int forceit, int writing));
- #endif
-
- static int check_overwrite __ARGS((exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int other));
- static int check_readonly __ARGS((int *forceit, buf_T *buf));
- #ifdef FEAT_AUTOCMD
- static void delbuf_msg __ARGS((char_u *name));
- #endif
- static int do_sub_msg __ARGS((void));
- static int
- #ifdef __BORLANDC__
- _RTLENTRYF
- #endif
- help_compare __ARGS((const void *s1, const void *s2));
-
- /*
- * ":ascii" and "ga".
- */
- /*ARGSUSED*/
- void
- do_ascii(eap)
- exarg_T *eap;
- {
- int c;
- char buf1[20];
- char buf2[20];
- char_u buf3[7];
- #ifdef FEAT_MBYTE
- int c1 = 0;
- int c2 = 0;
- int len;
-
- if (enc_utf8)
- c = utfc_ptr2char(ml_get_cursor(), &c1, &c2);
- else
- #endif
- c = gchar_cursor();
- if (c == NUL)
- {
- MSG("NUL");
- return;
- }
-
- #ifdef FEAT_MBYTE
- IObuff[0] = NUL;
- if (!has_mbyte || (enc_dbcs != 0 && c < 0x100) || c < 0x80)
- #endif
- {
- if (c == NL) /* NUL is stored as NL */
- c = NUL;
- if (vim_isprintc_strict(c) && (c < ' '
- #ifndef EBCDIC
- || c > '~'
- #endif
- ))
- {
- transchar_nonprint(buf3, c);
- sprintf(buf1, " <%s>", (char *)buf3);
- }
- else
- buf1[0] = NUL;
- #ifndef EBCDIC
- if (c >= 0x80)
- sprintf(buf2, " <M-%s>", transchar(c & 0x7f));
- else
- #endif
- buf2[0] = NUL;
- sprintf((char *)IObuff, _("<%s>%s%s %d, Hex %02x, Octal %03o"),
- transchar(c), buf1, buf2, c, c, c);
- #ifdef FEAT_MBYTE
- c = c1;
- c1 = c2;
- c2 = 0;
- #endif
- }
-
- #ifdef FEAT_MBYTE
- /* Repeat for combining characters. */
- while (has_mbyte && (c >= 0x100 || (enc_utf8 && c >= 0x80)))
- {
- len = (int)STRLEN(IObuff);
- /* This assumes every multi-byte char is printable... */
- if (len > 0)
- IObuff[len++] = ' ';
- IObuff[len++] = '<';
- if (utf_iscomposing(c)
- #ifdef USE_GUI
- && !gui.in_use
- #endif
- )
- IObuff[len++] = ' '; /* draw composing char on top of a space */
- IObuff[len + (*mb_char2bytes)(c, IObuff + len)] = NUL;
- if (c < 0x10000)
- sprintf((char *)IObuff + STRLEN(IObuff),
- "> %d, Hex %04x, Octal %o", c, c, c);
- else
- sprintf((char *)IObuff + STRLEN(IObuff),
- "> %d, Hex %08x, Octal %o", c, c, c);
- c = c1;
- c1 = c2;
- c2 = 0;
- }
- #endif
-
- msg(IObuff);
- }
-
- #if defined(FEAT_EX_EXTRA) || defined(PROTO)
- /*
- * ":left", ":center" and ":right": align text.
- */
- void
- ex_align(eap)
- exarg_T *eap;
- {
- pos_T save_curpos;
- int len;
- int indent = 0;
- int new_indent;
- int has_tab;
- int width;
-
- #ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
- {
- /* switch left and right aligning */
- if (eap->cmdidx == CMD_right)
- eap->cmdidx = CMD_left;
- else if (eap->cmdidx == CMD_left)
- eap->cmdidx = CMD_right;
- }
- #endif
-
- width = atoi((char *)eap->arg);
- save_curpos = curwin->w_cursor;
- if (eap->cmdidx == CMD_left) /* width is used for new indent */
- {
- if (width >= 0)
- indent = width;
- }
- else
- {
- /*
- * if 'textwidth' set, use it
- * else if 'wrapmargin' set, use it
- * if invalid value, use 80
- */
- if (width <= 0)
- width = curbuf->b_p_tw;
- if (width == 0 && curbuf->b_p_wm > 0)
- width = W_WIDTH(curwin) - curbuf->b_p_wm;
- if (width <= 0)
- width = 80;
- }
-
- if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
- return;
-
- for (curwin->w_cursor.lnum = eap->line1;
- curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum)
- {
- if (eap->cmdidx == CMD_left) /* left align */
- new_indent = indent;
- else
- {
- len = linelen(eap->cmdidx == CMD_right ? &has_tab
- : NULL) - get_indent();
-
- if (len <= 0) /* skip blank lines */
- continue;
-
- if (eap->cmdidx == CMD_center)
- new_indent = (width - len) / 2;
- else
- {
- new_indent = width - len; /* right align */
-
- /*
- * Make sure that embedded TABs don't make the text go too far
- * to the right.
- */
- if (has_tab)
- while (new_indent > 0)
- {
- (void)set_indent(new_indent, 0);
- if (linelen(NULL) <= width)
- {
- /*
- * Now try to move the line as much as possible to
- * the right. Stop when it moves too far.
- */
- do
- (void)set_indent(++new_indent, 0);
- while (linelen(NULL) <= width);
- --new_indent;
- break;
- }
- --new_indent;
- }
- }
- }
- if (new_indent < 0)
- new_indent = 0;
- (void)set_indent(new_indent, 0); /* set indent */
- }
- changed_lines(eap->line1, 0, eap->line2 + 1, 0L);
- curwin->w_cursor = save_curpos;
- beginline(BL_WHITE | BL_FIX);
- }
-
- /*
- * Get the length of the current line, excluding trailing white space.
- */
- static int
- linelen(has_tab)
- int *has_tab;
- {
- char_u *line;
- char_u *first;
- char_u *last;
- int save;
- int len;
-
- /* find the first non-blank character */
- line = ml_get_curline();
- first = skipwhite(line);
-
- /* find the character after the last non-blank character */
- for (last = first + STRLEN(first);
- last > first && vim_iswhite(last[-1]); --last)
- ;
- save = *last;
- *last = NUL;
- len = linetabsize(line); /* get line length */
- if (has_tab != NULL) /* check for embedded TAB */
- *has_tab = (vim_strrchr(first, TAB) != NULL);
- *last = save;
-
- return len;
- }
-
- /*
- * ":retab".
- */
- void
- ex_retab(eap)
- exarg_T *eap;
- {
- linenr_T lnum;
- int got_tab = FALSE;
- long num_spaces = 0;
- long num_tabs;
- long len;
- long col;
- long vcol;
- long start_col = 0; /* For start of white-space string */
- long start_vcol = 0; /* For start of white-space string */
- int temp;
- long old_len;
- char_u *ptr;
- char_u *new_line = (char_u *)1; /* init to non-NULL */
- int did_undo; /* called u_save for current line */
- int new_ts;
- int save_list;
- linenr_T first_line = 0; /* first changed line */
- linenr_T last_line = 0; /* last changed line */
-
- save_list = curwin->w_p_list;
- curwin->w_p_list = 0; /* don't want list mode here */
-
- new_ts = getdigits(&(eap->arg));
- if (new_ts == 0)
- new_ts = curbuf->b_p_ts;
- for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
- {
- ptr = ml_get(lnum);
- col = 0;
- vcol = 0;
- did_undo = FALSE;
- for (;;)
- {
- if (vim_iswhite(ptr[col]))
- {
- if (!got_tab && num_spaces == 0)
- {
- /* First consecutive white-space */
- start_vcol = vcol;
- start_col = col;
- }
- if (ptr[col] == ' ')
- num_spaces++;
- else
- got_tab = TRUE;
- }
- else
- {
- if (got_tab || (eap->forceit && num_spaces > 1))
- {
- /* Retabulate this string of white-space */
-
- /* len is virtual length of white string */
- len = num_spaces = vcol - start_vcol;
- num_tabs = 0;
- if (!curbuf->b_p_et)
- {
- temp = new_ts - (start_vcol % new_ts);
- if (num_spaces >= temp)
- {
- num_spaces -= temp;
- num_tabs++;
- }
- num_tabs += num_spaces / new_ts;
- num_spaces -= (num_spaces / new_ts) * new_ts;
- }
- if (curbuf->b_p_et || got_tab ||
- (num_spaces + num_tabs < len))
- {
- if (did_undo == FALSE)
- {
- did_undo = TRUE;
- if (u_save((linenr_T)(lnum - 1),
- (linenr_T)(lnum + 1)) == FAIL)
- {
- new_line = NULL; /* flag out-of-memory */
- break;
- }
- }
-
- /* len is actual number of white characters used */
- len = num_spaces + num_tabs;
- old_len = (long)STRLEN(ptr);
- new_line = lalloc(old_len - col + start_col + len + 1,
- TRUE);
- if (new_line == NULL)
- break;
- if (start_col > 0)
- mch_memmove(new_line, ptr, (size_t)start_col);
- mch_memmove(new_line + start_col + len,
- ptr + col, (size_t)(old_len - col + 1));
- ptr = new_line + start_col;
- for (col = 0; col < len; col++)
- ptr[col] = (col < num_tabs) ? '\t' : ' ';
- ml_replace(lnum, new_line, FALSE);
- if (first_line == 0)
- first_line = lnum;
- last_line = lnum;
- ptr = new_line;
- col = start_col + len;
- }
- }
- got_tab = FALSE;
- num_spaces = 0;
- }
- if (ptr[col] == NUL)
- break;
- vcol += chartabsize(ptr + col, (colnr_T)vcol);
- #ifdef FEAT_MBYTE
- if (has_mbyte)
- col += (*mb_ptr2len_check)(ptr + col);
- else
- #endif
- ++col;
- }
- if (new_line == NULL) /* out of memory */
- break;
- line_breakcheck();
- }
- if (got_int)
- EMSG(_(e_interr));
-
- if (curbuf->b_p_ts != new_ts)
- redraw_curbuf_later(NOT_VALID);
- if (first_line != 0)
- changed_lines(first_line, 0, last_line + 1, 0L);
-
- curwin->w_p_list = save_list; /* restore 'list' */
-
- curbuf->b_p_ts = new_ts;
- coladvance(curwin->w_curswant);
-
- u_clearline();
- }
- #endif
-
- /*
- * :move command - move lines line1-line2 to line dest
- *
- * return FAIL for failure, OK otherwise
- */
- int
- do_move(line1, line2, dest)
- linenr_T line1;
- linenr_T line2;
- linenr_T dest;
- {
- char_u *str;
- linenr_T l;
- linenr_T extra; /* Num lines added before line1 */
- linenr_T num_lines; /* Num lines moved */
- linenr_T last_line; /* Last line in file after adding new text */
-
- if (dest >= line1 && dest < line2)
- {
- EMSG(_("E134: Move lines into themselves"));
- return FAIL;
- }
-
- num_lines = line2 - line1 + 1;
-
- /*
- * First we copy the old text to its new location -- webb
- * Also copy the flag that ":global" command uses.
- */
- if (u_save(dest, dest + 1) == FAIL)
- return FAIL;
- for (extra = 0, l = line1; l <= line2; l++)
- {
- str = vim_strsave(ml_get(l + extra));
- if (str != NULL)
- {
- ml_append(dest + l - line1, str, (colnr_T)0, FALSE);
- vim_free(str);
- if (dest < line1)
- extra++;
- }
- }
-
- /*
- * Now we must be careful adjusting our marks so that we don't overlap our
- * mark_adjust() calls.
- *
- * We adjust the marks within the old text so that they refer to the
- * last lines of the file (temporarily), because we know no other marks
- * will be set there since these line numbers did not exist until we added
- * our new lines.
- *
- * Then we adjust the marks on lines between the old and new text positions
- * (either forwards or backwards).
- *
- * And Finally we adjust the marks we put at the end of the file back to
- * their final destination at the new text position -- webb
- */
- last_line = curbuf->b_ml.ml_line_count;
- mark_adjust(line1, line2, last_line - line2, 0L);
- if (dest >= line2)
- {
- mark_adjust(line2 + 1, dest, -num_lines, 0L);
- curbuf->b_op_start.lnum = dest - num_lines + 1;
- curbuf->b_op_end.lnum = dest;
- }
- else
- {
- mark_adjust(dest + 1, line1 - 1, num_lines, 0L);
- curbuf->b_op_start.lnum = dest + 1;
- curbuf->b_op_end.lnum = dest + num_lines;
- }
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
- mark_adjust(last_line - num_lines + 1, last_line,
- -(last_line - dest - extra), 0L);
-
- /*
- * Now we delete the original text -- webb
- */
- if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
- return FAIL;
-
- for (l = line1; l <= line2; l++)
- ml_delete(line1 + extra, TRUE);
-
- if (!global_busy && num_lines > p_report)
- {
- if (num_lines == 1)
- MSG(_("1 line moved"));
- else
- smsg((char_u *)_("%ld lines moved"), num_lines);
- }
-
- /*
- * Leave the cursor on the last of the moved lines.
- */
- if (dest >= line1)
- curwin->w_cursor.lnum = dest;
- else
- curwin->w_cursor.lnum = dest + (line2 - line1) + 1;
-
- if (line1 < dest)
- changed_lines(line1, 0, dest + num_lines + 1, 0L);
- else
- changed_lines(dest + 1, 0, line1 + num_lines, 0L);
-
- return OK;
- }
-
- /*
- * ":copy"
- */
- void
- ex_copy(line1, line2, n)
- linenr_T line1;
- linenr_T line2;
- linenr_T n;
- {
- linenr_T count;
- char_u *p;
-
- count = line2 - line1 + 1;
- curbuf->b_op_start.lnum = n + 1;
- curbuf->b_op_end.lnum = n + count;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
-
- /*
- * there are three situations:
- * 1. destination is above line1
- * 2. destination is between line1 and line2
- * 3. destination is below line2
- *
- * n = destination (when starting)
- * curwin->w_cursor.lnum = destination (while copying)
- * line1 = start of source (while copying)
- * line2 = end of source (while copying)
- */
- if (u_save(n, n + 1) == FAIL)
- return;
-
- curwin->w_cursor.lnum = n;
- while (line1 <= line2)
- {
- /* need to use vim_strsave() because the line will be unlocked within
- * ml_append() */
- p = vim_strsave(ml_get(line1));
- if (p != NULL)
- {
- ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE);
- vim_free(p);
- }
- /* situation 2: skip already copied lines */
- if (line1 == n)
- line1 = curwin->w_cursor.lnum;
- ++line1;
- if (curwin->w_cursor.lnum < line1)
- ++line1;
- if (curwin->w_cursor.lnum < line2)
- ++line2;
- ++curwin->w_cursor.lnum;
- }
-
- appended_lines_mark(n, count);
-
- msgmore((long)count);
- }
-
- /*
- * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
- * Bangs in the argument are replaced with the previously entered command.
- * Remember the argument.
- *
- * RISCOS: Bangs only replaced when followed by a space, since many
- * pathnames contain one.
- */
- void
- do_bang(addr_count, eap, forceit, do_in, do_out)
- int addr_count;
- exarg_T *eap;
- int forceit;
- int do_in, do_out;
- {
- char_u *arg = eap->arg; /* command */
- linenr_T line1 = eap->line1; /* start of range */
- linenr_T line2 = eap->line2; /* end of range */
- static char_u *prevcmd = NULL; /* the previous command */
- char_u *newcmd = NULL; /* the new command */
- int free_newcmd = FALSE; /* need to free() newcmd */
- int ins_prevcmd;
- char_u *t;
- char_u *p;
- char_u *trailarg;
- int len;
- int scroll_save = msg_scroll;
-
- /*
- * Disallow shell commands for "rvim".
- * Disallow shell commands from .exrc and .vimrc in current directory for
- * security reasons.
- */
- if (check_restricted() || check_secure())
- return;
-
- if (addr_count == 0) /* :! */
- {
- msg_scroll = FALSE; /* don't scroll here */
- autowrite_all();
- msg_scroll = scroll_save;
- }
-
- /*
- * Try to find an embedded bang, like in :!<cmd> ! [args]
- * (:!! is indicated by the 'forceit' variable)
- */
- ins_prevcmd = forceit;
- trailarg = arg;
- do
- {
- len = (int)STRLEN(trailarg) + 1;
- if (newcmd != NULL)
- len += (int)STRLEN(newcmd);
- if (ins_prevcmd)
- {
- if (prevcmd == NULL)
- {
- EMSG(_(e_noprev));
- vim_free(newcmd);
- return;
- }
- len += (int)STRLEN(prevcmd);
- }
- if ((t = alloc(len)) == NULL)
- {
- vim_free(newcmd);
- return;
- }
- *t = NUL;
- if (newcmd != NULL)
- STRCAT(t, newcmd);
- if (ins_prevcmd)
- STRCAT(t, prevcmd);
- p = t + STRLEN(t);
- STRCAT(t, trailarg);
- vim_free(newcmd);
- newcmd = t;
-
- /*
- * Scan the rest of the argument for '!', which is replaced by the
- * previous command. "\!" is replaced by "!" (this is vi compatible).
- */
- trailarg = NULL;
- while (*p)
- {
- if (*p == '!'
- #ifdef RISCOS
- && (p[1] == ' ' || p[1] == NUL)
- #endif
- )
- {
- if (p > newcmd && p[-1] == '\\')
- mch_memmove(p - 1, p, (size_t)(STRLEN(p) + 1));
- else
- {
- trailarg = p;
- *trailarg++ = NUL;
- ins_prevcmd = TRUE;
- break;
- }
- }
- ++p;
- }
- } while (trailarg != NULL);
-
- vim_free(prevcmd);
- prevcmd = newcmd;
-
- if (bangredo) /* put cmd in redo buffer for ! command */
- {
- AppendToRedobuffLit(prevcmd);
- AppendToRedobuff((char_u *)"\n");
- bangredo = FALSE;
- }
- /*
- * Add quotes around the command, for shells that need them.
- */
- if (*p_shq != NUL)
- {
- newcmd = alloc((unsigned)(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1));
- if (newcmd == NULL)
- return;
- STRCPY(newcmd, p_shq);
- STRCAT(newcmd, prevcmd);
- STRCAT(newcmd, p_shq);
- free_newcmd = TRUE;
- }
- if (addr_count == 0) /* :! */
- {
- /* echo the command */
- msg_start();
- msg_putchar(':');
- msg_putchar('!');
- msg_outtrans(newcmd);
- msg_clr_eos();
- windgoto(msg_row, msg_col);
-
- do_shell(newcmd, 0);
- }
- else /* :range! */
- /* Careful: This may recursively call do_bang() again! (because of
- * autocommands) */
- do_filter(line1, line2, eap, newcmd, do_in, do_out);
- if (free_newcmd)
- vim_free(newcmd);
- }
-
- /*
- * do_filter: filter lines through a command given by the user
- *
- * We use temp files and the call_shell() routine here. This would normally
- * be done using pipes on a UNIX machine, but this is more portable to
- * non-unix machines. The call_shell() routine needs to be able
- * to deal with redirection somehow, and should handle things like looking
- * at the PATH env. variable, and adding reasonable extensions to the
- * command name given by the user. All reasonable versions of call_shell()
- * do this.
- * We use input redirection if do_in is TRUE.
- * We use output redirection if do_out is TRUE.
- */
- static void
- do_filter(line1, line2, eap, cmd, do_in, do_out)
- linenr_T line1, line2;
- exarg_T *eap; /* for forced 'ff' and 'fenc' */
- char_u *cmd;
- int do_in, do_out;
- {
- char_u *itmp = NULL;
- char_u *otmp = NULL;
- linenr_T linecount;
- pos_T cursor_save;
- char_u *cmd_buf;
- #ifdef FEAT_AUTOCMD
- buf_T *old_curbuf = curbuf;
- #endif
-
- if (*cmd == NUL) /* no filter command */
- return;
-
- #ifdef WIN3264
- /*
- * Check if external commands are allowed now.
- */
- if (can_end_termcap_mode(TRUE) == FALSE)
- return;
- #endif
-
- cursor_save = curwin->w_cursor;
- linecount = line2 - line1 + 1;
- curwin->w_cursor.lnum = line1;
- curwin->w_cursor.col = 0;
- changed_line_abv_curs();
- invalidate_botline();
-
- /*
- * 1. Form temp file names
- * 2. Write the lines to a temp file
- * 3. Run the filter command on the temp file
- * 4. Read the output of the command into the buffer
- * 5. Delete the original lines to be filtered
- * 6. Remove the temp files
- */
-
- if ((do_in && (itmp = vim_tempname('i')) == NULL)
- || (do_out && (otmp = vim_tempname('o')) == NULL))
- {
- EMSG(_(e_notmp));
- goto filterend;
- }
-
- /*
- * The writing and reading of temp files will not be shown.
- * Vi also doesn't do this and the messages are not very informative.
- */
- ++no_wait_return; /* don't call wait_return() while busy */
- if (do_in && buf_write(curbuf, itmp, NULL, line1, line2, eap,
- FALSE, FALSE, FALSE, TRUE) == FAIL)
- {
- msg_putchar('\n'); /* keep message from buf_write() */
- --no_wait_return;
- (void)EMSG2(_(e_notcreate), itmp); /* will call wait_return */
- goto filterend;
- }
- #ifdef FEAT_AUTOCMD
- if (curbuf != old_curbuf)
- goto filterend;
- #endif
-
- if (!do_out)
- msg_putchar('\n');
-
- cmd_buf = make_filter_cmd(cmd, itmp, otmp);
- if (cmd_buf == NULL)
- goto filterend;
-
- windgoto((int)Rows - 1, 0);
- cursor_on();
-
- /*
- * When not redirecting the output the command can write anything to the
- * screen. If 'shellredir' is equal to ">", screen may be messed up by
- * stderr output of external command. Clear the screen later.
- * If do_in is FALSE, this could be something like ":r !cat", which may
- * also mess up the screen, clear it later.
- */
- if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in)
- redraw_later_clear();
-
- /*
- * When call_shell() fails wait_return() is called to give the user a
- * chance to read the error messages. Otherwise errors are ignored, so you
- * can see the error messages from the command that appear on stdout; use
- * 'u' to fix the text
- * Switch to cooked mode when not redirecting stdin, avoids that something
- * like ":r !cat" hangs.
- * Pass on the SHELL_DOOUT flag when the output is being redirected.
- */
- if (call_shell(cmd_buf, SHELL_FILTER | SHELL_COOKED
- | (do_out ? SHELL_DOOUT : 0)))
- {
- redraw_later_clear();
- wait_return(FALSE);
- }
- vim_free(cmd_buf);
-
- did_check_timestamps = FALSE;
- need_check_timestamps = TRUE;
-
- if (do_out)
- {
- if (u_save((linenr_T)(line2), (linenr_T)(line2 + 1)) == FAIL)
- goto error;
- redraw_curbuf_later(VALID);
- if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- READ_FILTER) == FAIL)
- {
- msg_putchar('\n');
- EMSG2(_(e_notread), otmp);
- goto error;
- }
- #ifdef FEAT_AUTOCMD
- if (curbuf != old_curbuf)
- goto filterend;
- #endif
-
- if (do_in)
- {
- /*
- * Put cursor on first filtered line for ":range!cmd".
- * Adjust '[ and '] (set by buf_write()).
- */
- curwin->w_cursor.lnum = line1;
- del_lines(linecount, TRUE);
- curbuf->b_op_start.lnum -= linecount; /* adjust '[ */
- curbuf->b_op_end.lnum -= linecount; /* adjust '] */
- write_lnum_adjust(-linecount); /* adjust last line
- for next write */
- }
- else
- {
- /*
- * Put cursor on last new line for ":r !cmd".
- */
- curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
- linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
- }
- beginline(BL_WHITE | BL_FIX); /* cursor on first non-blank */
- --no_wait_return;
-
- if (linecount > p_report)
- {
- if (do_in)
- {
- sprintf((char *)msg_buf, _("%ld lines filtered"),
- (long)linecount);
- if (msg(msg_buf) && !msg_scroll)
- {
- /* save message to display it after redraw */
- set_keep_msg(msg_buf);
- keep_msg_attr = 0;
- }
- }
- else
- msgmore((long)linecount);
- }
- }
- else
- {
- error:
- /* put cursor back in same position for ":w !cmd" */
- curwin->w_cursor = cursor_save;
- --no_wait_return;
- wait_return(FALSE);
- }
-
- filterend:
-
- #ifdef FEAT_AUTOCMD
- if (curbuf != old_curbuf)
- {
- --no_wait_return;
- EMSG(_("E135: *Filter* Autocommands must not change current buffer"));
- }
- #endif
- if (itmp != NULL)
- mch_remove(itmp);
- if (otmp != NULL)
- mch_remove(otmp);
- vim_free(itmp);
- vim_free(otmp);
- }
-
- /*
- * Call a shell to execute a command.
- * When "cmd" is NULL start an interactive shell.
- */
- void
- do_shell(cmd, flags)
- char_u *cmd;
- int flags; /* may be SHELL_DOOUT when output is redirected */
- {
- buf_T *buf;
- #ifndef FEAT_GUI_MSWIN
- int save_nwr;
- #endif
- #ifdef MSWIN
- int winstart = FALSE;
- #endif
-
- /*
- * Disallow shell commands for "rvim".
- * Disallow shell commands from .exrc and .vimrc in current directory for
- * security reasons.
- */
- if (check_restricted() || check_secure())
- {
- msg_end();
- return;
- }
-
- #ifdef MSWIN
- /*
- * Check if external commands are allowed now.
- */
- if (can_end_termcap_mode(TRUE) == FALSE)
- return;
-
- /*
- * Check if ":!start" is used.
- */
- if (cmd != NULL)
- winstart = (STRNICMP(cmd, "start ", 6) == 0);
- #endif
-
- /*
- * For autocommands we want to get the output on the current screen, to
- * avoid having to type return below.
- */
- msg_putchar('\r'); /* put cursor at start of line */
- #ifdef FEAT_AUTOCMD
- if (!autocmd_busy)
- #endif
- {
- #ifdef MSWIN
- if (!winstart)
- #endif
- stoptermcap();
- }
- #ifdef MSWIN
- if (!winstart)
- #endif
- msg_putchar('\n'); /* may shift screen one line up */
-
- /* warning message before calling the shell */
- if (p_warn
- #ifdef FEAT_AUTOCMD
- && !autocmd_busy
- #endif
- && msg_silent == 0)
- for (buf = firstbuf; buf; buf = buf->b_next)
- if (bufIsChanged(buf))
- {
- #ifdef FEAT_GUI_MSWIN
- if (!winstart)
- starttermcap(); /* don't want a message box here */
- #endif
- MSG_PUTS(_("[No write since last change]\n"));
- #ifdef FEAT_GUI_MSWIN
- if (!winstart)
- stoptermcap();
- #endif
- break;
- }
-
- /* This windgoto is required for when the '\n' resulted in a "delete line
- * 1" command to the terminal. */
- if (!swapping_screen())
- windgoto(msg_row, msg_col);
- cursor_on();
- (void)call_shell(cmd, SHELL_COOKED | flags);
- did_check_timestamps = FALSE;
- need_check_timestamps = TRUE;
-
- /*
- * put the message cursor at the end of the screen, avoids wait_return()
- * to overwrite the text that the external command showed
- */
- if (!swapping_screen())
- {
- msg_row = Rows - 1;
- msg_col = 0;
- }
-
- #ifdef FEAT_AUTOCMD
- if (autocmd_busy)
- {
- if (msg_silent == 0)
- redraw_later_clear();
- }
- else
- #endif
- {
- /*
- * For ":sh" there is no need to call wait_return(), just redraw.
- * Also for the Win32 GUI (the output is in a console window).
- * Otherwise there is probably text on the screen that the user wants
- * to read before redrawing, so call wait_return().
- */
- #ifndef FEAT_GUI_MSWIN
- if (cmd == NULL
- # ifdef WIN3264
- || (winstart && !need_wait_return)
- # endif
- )
- {
- if (msg_silent == 0)
- redraw_later_clear();
- need_wait_return = FALSE;
- }
- else
- {
- /*
- * If we switch screens when starttermcap() is called, we really
- * want to wait for "hit return to continue".
- */
- save_nwr = no_wait_return;
- if (swapping_screen())
- no_wait_return = FALSE;
- # ifdef AMIGA
- wait_return(term_console ? -1 : msg_silent == 0); /* see below */
- # else
- wait_return(msg_silent == 0);
- # endif
- no_wait_return = save_nwr;
- }
- #endif /* FEAT_GUI_W32 */
-
- #ifdef MSWIN
- if (!winstart) /* if winstart==TRUE, never stopped termcap! */
- #endif
- starttermcap(); /* start termcap if not done by wait_return() */
-
- /*
- * In an Amiga window redrawing is caused by asking the window size.
- * If we got an interrupt this will not work. The chance that the
- * window size is wrong is very small, but we need to redraw the
- * screen. Don't do this if ':' hit in wait_return(). THIS IS UGLY
- * but it saves an extra redraw.
- */
- #ifdef AMIGA
- if (skip_redraw) /* ':' hit in wait_return() */
- {
- if (msg_silent == 0)
- redraw_later_clear();
- }
- else if (term_console)
- {
- OUT_STR(IF_EB("\033[0 q", ESC_STR "[0 q")); /* get window size */
- if (got_int && msg_silent == 0)
- redraw_later_clear(); /* if got_int is TRUE, redraw needed */
- else
- must_redraw = 0; /* no extra redraw needed */
- }
- #endif
- }
-
- /* display any error messages now */
- display_errors();
- }
-
- /*
- * Create a shell command from a command string, input redirection file and
- * output redirection file.
- * Returns an allocated string with the shell command, or NULL for failure.
- */
- char_u *
- make_filter_cmd(cmd, itmp, otmp)
- char_u *cmd; /* command */
- char_u *itmp; /* NULL or name of input file */
- char_u *otmp; /* NULL or name of output file */
- {
- char_u *buf;
- long_u len;
-
- len = (long_u)STRLEN(cmd) + 3; /* "()" + NUL */
- if (itmp != NULL)
- len += (long_u)STRLEN(itmp) + 9; /* " { < " + " } " */
- if (otmp != NULL)
- len += (long_u)STRLEN(otmp) + (long_u)STRLEN(p_srr) + 2; /* " " */
- buf = lalloc(len, TRUE);
- if (buf == NULL)
- return NULL;
-
- #if (defined(UNIX) && !defined(ARCHIE)) || defined(OS2)
- /*
- * put braces around the command (for concatenated commands)
- */
- sprintf((char *)buf, "(%s)", (char *)cmd);
- if (itmp != NULL)
- {
- STRCAT(buf, " < ");
- STRCAT(buf, itmp);
- }
- #else
- /*
- * for shells that don't understand braces around commands, at least allow
- * the use of commands in a pipe.
- */
- STRCPY(buf, cmd);
- if (itmp != NULL)
- {
- char_u *p;
-
- /*
- * If there is a pipe, we have to put the '<' in front of it.
- * Don't do this when 'shellquote' is not empty, otherwise the
- * redirection would be inside the quotes.
- */
- if (*p_shq == NUL)
- {
- p = vim_strchr(buf, '|');
- if (p != NULL)
- *p = NUL;
- }
- # ifdef RISCOS
- STRCAT(buf, " { < "); /* Use RISC OS notation for input. */
- STRCAT(buf, itmp);
- STRCAT(buf, " } ");
- # else
- STRCAT(buf, " <"); /* " < " causes problems on Amiga */
- STRCAT(buf, itmp);
- # endif
- if (*p_shq == NUL)
- {
- p = vim_strchr(cmd, '|');
- if (p != NULL)
- {
- STRCAT(buf, " "); /* insert a space before the '|' for DOS */
- STRCAT(buf, p);
- }
- }
- }
- #endif
- if (otmp != NULL)
- append_redir(buf, p_srr, otmp);
-
- return buf;
- }
-
- /*
- * Append output redirection for file "fname" to the end of string buffer "buf"
- * Works with the 'shellredir' and 'shellpipe' options.
- * The caller should make sure that there is enough room:
- * STRLEN(opt) + STRLEN(fname) + 3
- */
- void
- append_redir(buf, opt, fname)
- char_u *buf;
- char_u *opt;
- char_u *fname;
- {
- char_u *p;
-
- buf += STRLEN(buf);
- /* find "%s", skipping "%%" */
- for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p)
- if (p[1] == 's')
- break;
- if (p != NULL)
- {
- *buf = ' '; /* not really needed? Not with sh, ksh or bash */
- sprintf((char *)buf + 1, (char *)opt, (char *)fname);
- }
- else
- sprintf((char *)buf,
- #ifdef FEAT_QUICKFIX
- # ifndef RISCOS
- opt != p_sp ? " %s%s" :
- # endif
- " %s %s",
- #else
- # ifndef RISCOS
- " %s%s", /* " > %s" causes problems on Amiga */
- # else
- " %s %s", /* But is needed for 'shellpipe' and RISC OS */
- # endif
- #endif
- (char *)opt, (char *)fname);
- }
-
- #ifdef FEAT_VIMINFO
-
- static int no_viminfo __ARGS((void));
- static int viminfo_errcnt;
-
- static int
- no_viminfo()
- {
- /* "vim -i NONE" does not read or write a viminfo file */
- return (use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0);
- }
-
- /*
- * Report an error for reading a viminfo file.
- * Count the number of errors. When there are more than 10, return TRUE.
- */
- int
- viminfo_error(message, line)
- char *message;
- char_u *line;
- {
- sprintf((char *)IObuff, _("viminfo: %s in line: "), message);
- STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff));
- emsg(IObuff);
- if (++viminfo_errcnt >= 10)
- {
- EMSG(_("E136: viminfo: Too many errors, skipping rest of file"));
- return TRUE;
- }
- return FALSE;
- }
-
- /*
- * read_viminfo() -- Read the viminfo file. Registers etc. which are already
- * set are not over-written unless force is TRUE. -- webb
- */
- int
- read_viminfo(file, want_info, want_marks, forceit)
- char_u *file;
- int want_info;
- int want_marks;
- int forceit;
- {
- FILE *fp;
- char_u *fname;
-
- if (no_viminfo())
- return FAIL;
-
- fname = viminfo_filename(file); /* may set to default if NULL */
- if (fname == NULL)
- return FAIL;
- fp = mch_fopen((char *)fname, READBIN);
-
- if (p_verbose > 0)
- smsg((char_u *)_("Reading viminfo file \"%s\"%s%s%s"), fname,
- want_info ? _(" info") : "",
- want_marks ? _(" marks") : "",
- fp == NULL ? _(" FAILED") : "");
-
- vim_free(fname);
- if (fp == NULL)
- return FAIL;
-
- viminfo_errcnt = 0;
- do_viminfo(fp, NULL, want_info, want_marks, forceit);
-
- fclose(fp);
-
- return OK;
- }
-
- /*
- * write_viminfo() -- Write the viminfo file. The old one is read in first so
- * that effectively a merge of current info and old info is done. This allows
- * multiple vims to run simultaneously, without losing any marks etc. If
- * forceit is TRUE, then the old file is not read in, and only internal info is
- * written to the file. -- webb
- */
- void
- write_viminfo(file, forceit)
- char_u *file;
- int forceit;
- {
- char_u *fname;
- FILE *fp_in = NULL; /* input viminfo file, if any */
- FILE *fp_out = NULL; /* output viminfo file */
- char_u *tempname = NULL; /* name of temp viminfo file */
- struct stat st_new; /* mch_stat() of potential new file */
- char_u *wp;
- #if defined(UNIX) || defined(VMS)
- mode_t umask_save;
- #endif
- #ifdef UNIX
- int shortname = FALSE; /* use 8.3 file name */
- struct stat st_old; /* mch_stat() of existing viminfo file */
- #endif
-
- if (no_viminfo())
- return;
-
- fname = viminfo_filename(file); /* may set to default if NULL */
- if (fname == NULL)
- return;
-
- fp_in = mch_fopen((char *)fname, READBIN);
- if (fp_in == NULL)
- {
- /* if it does exist, but we can't read it, don't try writing */
- if (mch_stat((char *)fname, &st_new) == 0)
- goto end;
- #if defined(UNIX) || defined(VMS)
- /*
- * For Unix we create the .viminfo non-accessible for others,
- * because it may contain text from non-accessible documents.
- */
- umask_save = umask(077);
- #endif
- fp_out = mch_fopen((char *)fname, WRITEBIN);
- #if defined(UNIX) || defined(VMS)
- (void)umask(umask_save);
- #endif
- }
- else
- {
- /*
- * There is an existing viminfo file. Create a temporary file to
- * write the new viminfo into, in the same directory as the
- * existing viminfo file, which will be renamed later.
- */
- #ifdef UNIX
- /*
- * For Unix we check the owner of the file. It's not very nice to
- * overwrite a user's viminfo file after a "su root", with a
- * viminfo file that the user can't read.
- */
- st_old.st_dev = st_old.st_ino = 0;
- st_old.st_mode = 0600;
- if (mch_stat((char *)fname, &st_old) == 0 && getuid() &&
- !(st_old.st_uid == getuid()
- ? (st_old.st_mode & 0200)
- : (st_old.st_gid == getgid()
- ? (st_old.st_mode & 0020)
- : (st_old.st_mode & 0002))))
- {
- int tt;
-
- /* avoid a wait_return for this message, it's annoying */
- tt = msg_didany;
- EMSG2(_("E137: Viminfo file is not writable: %s"), fname);
- msg_didany = tt;
- goto end;
- }
- #endif
-
- /*
- * Make tempname.
- * May try twice: Once normal and once with shortname set, just in
- * case somebody puts his viminfo file in an 8.3 filesystem.
- */
- for (;;)
- {
- tempname = buf_modname(
- #ifdef UNIX
- shortname,
- #else
- # ifdef SHORT_FNAME
- TRUE,
- # else
- # ifdef FEAT_GUI_W32
- gui_is_win32s(),
- # else
- FALSE,
- # endif
- # endif
- #endif
- fname,
- #ifdef VMS
- (char_u *)"-tmp",
- #else
- # ifdef RISCOS
- (char_u *)"/tmp",
- # else
- (char_u *)".tmp",
- # endif
- #endif
- FALSE);
- if (tempname == NULL) /* out of memory */
- break;
-
- /*
- * Check if tempfile already exists. Never overwrite an
- * existing file!
- */
- if (mch_stat((char *)tempname, &st_new) == 0)
- {
- #ifdef UNIX
- /*
- * Check if tempfile is same as original file. May happen
- * when modname() gave the same file back. E.g. silly
- * link, or file name-length reached. Try again with
- * shortname set.
- */
- if (!shortname && st_new.st_dev == st_old.st_dev &&
- st_new.st_ino == st_old.st_ino)
- {
- vim_free(tempname);
- tempname = NULL;
- shortname = TRUE;
- continue;
- }
- #endif
- /*
- * Try another name. Change one character, just before
- * the extension. This should also work for an 8.3
- * file name, when after adding the extension it still is
- * the same file as the original.
- */
- wp = tempname + STRLEN(tempname) - 5;
- if (wp < gettail(tempname)) /* empty file name? */
- wp = gettail(tempname);
- for (*wp = 'z'; mch_stat((char *)tempname, &st_new) == 0;
- --*wp)
- {
- /*
- * They all exist? Must be something wrong! Don't
- * write the viminfo file then.
- */
- if (*wp == 'a')
- {
- vim_free(tempname);
- tempname = NULL;
- break;
- }
- }
- }
- break;
- }
-
- if (tempname != NULL)
- {
- fp_out = mch_fopen((char *)tempname, WRITEBIN);
-
- /*
- * If we can't create in the same directory, try creating a
- * "normal" temp file.
- */
- if (fp_out == NULL)
- {
- vim_free(tempname);
- if ((tempname = vim_tempname('o')) != NULL)
- fp_out = mch_fopen((char *)tempname, WRITEBIN);
- }
- #ifdef UNIX
- /*
- * Set file protection same as original file, but strip s-bit
- * and make sure the owner can read/write it.
- */
- if (fp_out != NULL)
- {
- (void)mch_setperm(tempname,
- (long)((st_old.st_mode & 0777) | 0600));
- /* this only works for root: */
- (void)chown((char *)tempname, st_old.st_uid, st_old.st_gid);
- }
- #endif
- }
- }
-
- /*
- * Check if the new viminfo file can be written to.
- */
- if (fp_out == NULL)
- {
- EMSG2(_("E138: Can't write viminfo file %s!"),
- fp_in == NULL ? fname : tempname);
- if (fp_in != NULL)
- fclose(fp_in);
- goto end;
- }
-
- if (p_verbose > 0)
- smsg((char_u *)_("Writing viminfo file \"%s\""), fname);
-
- viminfo_errcnt = 0;
- do_viminfo(fp_in, fp_out, !forceit, !forceit, FALSE);
-
- fclose(fp_out); /* errors are ignored !? */
- if (fp_in != NULL)
- {
- fclose(fp_in);
- /*
- * In case of an error, don't overwrite the original viminfo file.
- */
- if (viminfo_errcnt || vim_rename(tempname, fname) == -1)
- mch_remove(tempname);
- }
- end:
- vim_free(fname);
- vim_free(tempname);
- }
-
- /*
- * Get the viminfo file name to use.
- * If "file" is given and not empty, use it (has already been expanded by
- * cmdline functions).
- * Otherwise use "-i file_name", value from 'viminfo' or the default, and
- * expand environment variables.
- * Returns an allocated string. NULL when out of memory.
- */
- static char_u *
- viminfo_filename(file)
- char_u *file;
- {
- if (file == NULL || *file == NUL)
- {
- if (use_viminfo != NULL)
- file = use_viminfo;
- else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL)
- {
- #ifdef VIMINFO_FILE2
- /* don't use $HOME when not defined (turned into "c:/"!). */
- # ifdef VMS
- if (mch_getenv((char_u *)"SYS$LOGIN") == NULL)
- # else
- if (mch_getenv((char_u *)"HOME") == NULL)
- # endif
- {
- /* don't use $VIM when not available. */
- expand_env((char_u *)"$VIM", NameBuff, MAXPATHL);
- if (STRCMP("$VIM", NameBuff) != 0) /* $VIM was expanded */
- file = (char_u *)VIMINFO_FILE2;
- else
- file = (char_u *)VIMINFO_FILE;
- }
- else
- #endif
- file = (char_u *)VIMINFO_FILE;
- }
- expand_env(file, NameBuff, MAXPATHL);
- file = NameBuff;
- }
- return vim_strsave(file);
- }
-
- /*
- * do_viminfo() -- Should only be called from read_viminfo() & write_viminfo().
- */
- static void
- do_viminfo(fp_in, fp_out, want_info, want_marks, force_read)
- FILE *fp_in;
- FILE *fp_out;
- int want_info;
- int want_marks;
- int force_read;
- {
- int count = 0;
- int eof = FALSE;
- vir_T vir;
-
- if ((vir.vir_line = alloc(LSIZE)) == NULL)
- return;
- vir.vir_fd = fp_in;
- #ifdef FEAT_MBYTE
- vir.vir_conv.vc_type = CONV_NONE;
- # ifdef USE_ICONV
- vir.vir_conv.vc_fd = (iconv_t)-1;
- # endif
- #endif
-
- if (fp_in != NULL)
- {
- if (want_info)
- eof = read_viminfo_up_to_marks(&vir, force_read, fp_out != NULL);
- else
- /* Skip info, find start of marks */
- while (!(eof = viminfo_readline(&vir))
- && vir.vir_line[0] != '>')
- ;
- }
- if (fp_out != NULL)
- {
- /* Write the info: */
- fprintf(fp_out, _("# This viminfo file was generated by Vim %s.\n"),
- VIM_VERSION_MEDIUM);
- fprintf(fp_out, _("# You may edit it if you're careful!\n\n"));
- #ifdef FEAT_MBYTE
- fprintf(fp_out, _("# Value of 'encoding' when this file was written\n"));
- fprintf(fp_out, "*encoding=%s\n\n", p_enc);
- #endif
- write_viminfo_search_pattern(fp_out);
- write_viminfo_sub_string(fp_out);
- #ifdef FEAT_CMDHIST
- write_viminfo_history(fp_out);
- #endif
- write_viminfo_registers(fp_out);
- #ifdef FEAT_EVAL
- write_viminfo_varlist(fp_out);
- #endif
- write_viminfo_filemarks(fp_out);
- write_viminfo_bufferlist(fp_out);
- count = write_viminfo_marks(fp_out);
- }
- if (fp_in != NULL && want_marks)
- copy_viminfo_marks(&vir, fp_out, count, eof);
-
- vim_free(vir.vir_line);
- #ifdef FEAT_MBYTE
- if (vir.vir_conv.vc_type != CONV_NONE)
- convert_setup(&vir.vir_conv, (char_u *)"", (char_u *)"");
- #endif
- }
-
- /*
- * read_viminfo_up_to_marks() -- Only called from do_viminfo(). Reads in the
- * first part of the viminfo file which contains everything but the marks that
- * are local to a file. Returns TRUE when end-of-file is reached. -- webb
- */
- static int
- read_viminfo_up_to_marks(virp, forceit, writing)
- vir_T *virp;
- int forceit;
- int writing;
- {
- int eof;
- buf_T *buf;
-
- #ifdef FEAT_CMDHIST
- prepare_viminfo_history(forceit ? 9999 : 0);
- #endif
- eof = viminfo_readline(virp);
- while (!eof && virp->vir_line[0] != '>')
- {
- switch (virp->vir_line[0])
- {
- /* Characters reserved for future expansion, ignored now */
- case '+': /* "+40 /path/dir file", for running vim without args */
- case '|': /* to be defined */
- case '^': /* to be defined */
- case '<': /* long line - ignored */
- /* A comment or empty line. */
- case NUL:
- case '\r':
- case '\n':
- case '#':
- eof = viminfo_readline(virp);
- break;
- case '*': /* "*encoding=value" */
- eof = viminfo_encoding(virp);
- break;
- case '!': /* global variable */
- #ifdef FEAT_EVAL
- eof = read_viminfo_varlist(virp, writing);
- #else
- eof = viminfo_readline(virp);
- #endif
- break;
- case '%': /* entry for buffer list */
- eof = read_viminfo_bufferlist(virp, writing);
- break;
- case '"':
- eof = read_viminfo_register(virp, forceit);
- break;
- case '/': /* Search string */
- case '&': /* Substitute search string */
- case '~': /* Last search string, followed by '/' or '&' */
- eof = read_viminfo_search_pattern(virp, forceit);
- break;
- case '$':
- eof = read_viminfo_sub_string(virp, forceit);
- break;
- case ':':
- case '?':
- case '=':
- case '@':
- #ifdef FEAT_CMDHIST
- eof = read_viminfo_history(virp);
- #else
- eof = viminfo_readline(virp);
- #endif
- break;
- case '-':
- case '\'':
- eof = read_viminfo_filemark(virp, forceit);
- break;
- default:
- if (viminfo_error(_("Illegal starting char"), virp->vir_line))
- eof = TRUE;
- else
- eof = viminfo_readline(virp);
- break;
- }
- }
-
- #ifdef FEAT_CMDHIST
- /* Finish reading history items. */
- finish_viminfo_history();
- #endif
-
- /* Change file names to buffer numbers for fmarks. */
- for (buf = firstbuf; buf != NULL; buf = buf->b_next)
- fmarks_check_names(buf);
-
- return eof;
- }
-
- /*
- * Compare the 'encoding' value in the viminfo file with the current value of
- * 'encoding'. If different and the 'c' flag is in 'viminfo', setup for
- * conversion of text with iconv() in viminfo_readstring().
- */
- static int
- viminfo_encoding(virp)
- vir_T *virp;
- {
- #ifdef FEAT_MBYTE
- char_u *p;
- int i;
-
- if (get_viminfo_parameter('c') != 0)
- {
- p = vim_strchr(virp->vir_line, '=');
- if (p != NULL)
- {
- /* remove trailing newline */
- ++p;
- for (i = 0; vim_isprintc(p[i]); ++i)
- ;
- p[i] = NUL;
-
- convert_setup(&virp->vir_conv, p, p_enc);
- }
- }
- #endif
- return viminfo_readline(virp);
- }
-
- /*
- * Read a line from the viminfo file.
- * Returns TRUE for end-of-file;
- */
- int
- viminfo_readline(virp)
- vir_T *virp;
- {
- return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
- }
-
- /*
- * check string read from viminfo file
- * remove '\n' at the end of the line
- * - replace CTRL-V CTRL-V with CTRL-V
- * - replace CTRL-V 'n' with '\n'
- *
- * Check for a long line as written by viminfo_writestring().
- *
- * Return the string in allocated memory (NULL when out of memory).
- */
- /*ARGSUSED*/
- char_u *
- viminfo_readstring(virp, off, convert)
- vir_T *virp;
- int off; /* offset for virp->vir_line */
- int convert; /* convert the string */
- {
- char_u *retval;
- char_u *s, *d;
- long len;
-
- if (virp->vir_line[off] == Ctrl_V && isdigit(virp->vir_line[off + 1]))
- {
- len = atol((char *)virp->vir_line + off + 1);
- retval = lalloc(len, TRUE);
- if (retval == NULL)
- {
- /* Line too long? File messed up? Skip next line. */
- (void)vim_fgets(virp->vir_line, 10, virp->vir_fd);
- return NULL;
- }
- (void)vim_fgets(retval, (int)len, virp->vir_fd);
- s = retval + 1; /* Skip the leading '<' */
- }
- else
- {
- retval = vim_strsave(virp->vir_line + off);
- if (retval == NULL)
- return NULL;
- s = retval;
- }
-
- /* Change CTRL-V CTRL-V to CTRL-V and CTRL-V n to \n in-place. */
- d = retval;
- while (*s != NUL && *s != '\n')
- {
- if (s[0] == Ctrl_V && s[1] != NUL)
- {
- if (s[1] == 'n')
- *d++ = '\n';
- else
- *d++ = Ctrl_V;
- s += 2;
- }
- else
- *d++ = *s++;
- }
- *d = NUL;
-
- #ifdef FEAT_MBYTE
- if (convert && virp->vir_conv.vc_type != CONV_NONE)
- {
- d = string_convert(&virp->vir_conv, retval, NULL);
- if (d != NULL)
- {
- vim_free(retval);
- retval = d;
- }
- }
- #endif
-
- return retval;
- }
-
- /*
- * write string to viminfo file
- * - replace CTRL-V with CTRL-V CTRL-V
- * - replace '\n' with CTRL-V 'n'
- * - add a '\n' at the end
- *
- * For a long line:
- * - write " CTRL-V <length> \n " in first line
- * - write " < <string> \n " in second line
- */
- void
- viminfo_writestring(fd, p)
- FILE *fd;
- char_u *p;
- {
- int c;
- char_u *s;
- int len = 0;
-
- for (s = p; *s != NUL; ++s)
- {
- if (*s == Ctrl_V || *s == '\n')
- ++len;
- ++len;
- }
-
- /* If the string will be too long, write its length and put it in the next
- * line. Take into account that some room is needed for what comes before
- * the string (e.g., variable name). Add something to the length for the
- * '<', NL and trailing NUL. */
- if (len > LSIZE / 2)
- fprintf(fd, IF_EB("\026%d\n<", CTRL_V_STR "%d\n<"), len + 3);
-
- while ((c = *p++) != NUL)
- {
- if (c == Ctrl_V || c == '\n')
- {
- putc(Ctrl_V, fd);
- if (c == '\n')
- c = 'n';
- }
- putc(c, fd);
- }
- putc('\n', fd);
- }
- #endif /* FEAT_VIMINFO */
-
- /*
- * Implementation of ":fixdel", also used by get_stty().
- * <BS> resulting <Del>
- * ^? ^H
- * not ^? ^?
- */
- /*ARGSUSED*/
- void
- do_fixdel(eap)
- exarg_T *eap;
- {
- char_u *p;
-
- p = find_termcode((char_u *)"kb");
- add_termcode((char_u *)"kD", p != NULL
- && *p == DEL ? (char_u *)CTRL_H_STR : DEL_STR, FALSE);
- }
-
- void
- print_line_no_prefix(lnum, use_number)
- linenr_T lnum;
- int use_number;
- {
- char_u numbuf[20];
-
- if (curwin->w_p_nu || use_number)
- {
- sprintf((char *)numbuf, "%7ld ", (long)lnum);
- msg_puts_attr(numbuf, hl_attr(HLF_N)); /* Highlight line nrs */
- }
- msg_prt_line(ml_get(lnum));
- }
-
- /*
- * Print a text line. Also in silent mode ("ex -s").
- */
- void
- print_line(lnum, use_number)
- linenr_T lnum;
- int use_number;
- {
- int save_silent = silent_mode;
-
- silent_mode = FALSE;
- msg_start();
- print_line_no_prefix(lnum, use_number);
- if (save_silent)
- {
- msg_putchar('\n');
- cursor_on(); /* msg_start() switches it off */
- out_flush();
- silent_mode = save_silent;
- }
- }
-
- /*
- * ":file[!] [fname]".
- */
- void
- ex_file(eap)
- exarg_T *eap;
- {
- char_u *fname, *sfname, *xfname;
- buf_T *buf;
-
- if (*eap->arg != NUL)
- {
- #ifdef FEAT_AUTOCMD
- buf = curbuf;
- apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
- /* buffer changed, don't change name now */
- if (buf != curbuf)
- return;
- #endif
- /*
- * The name of the current buffer will be changed.
- * A new buffer entry needs to be made to hold the old
- * file name, which will become the alternate file name.
- */
- fname = curbuf->b_ffname;
- sfname = curbuf->b_sfname;
- xfname = curbuf->b_fname;
- curbuf->b_ffname = NULL;
- curbuf->b_sfname = NULL;
- if (setfname(eap->arg, NULL, TRUE) == FAIL)
- {
- curbuf->b_ffname = fname;
- curbuf->b_sfname = sfname;
- return;
- }
- curbuf->b_flags |= BF_NOTEDITED;
- buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, BLN_LISTED);
- if (buf != NULL)
- curwin->w_alt_fnum = buf->b_fnum;
- vim_free(fname);
- vim_free(sfname);
- #ifdef FEAT_AUTOCMD
- apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
- #endif
- }
- /* print full file name if :cd used */
- fileinfo(FALSE, FALSE, eap->forceit);
- }
-
- /*
- * ":update".
- */
- void
- ex_update(eap)
- exarg_T *eap;
- {
- if (curbufIsChanged())
- (void)do_write(eap);
- }
-
- /*
- * ":write" and ":saveas".
- */
- void
- ex_write(eap)
- exarg_T *eap;
- {
- if (eap->usefilter) /* input lines to shell command */
- do_bang(1, eap, FALSE, TRUE, FALSE);
- else
- (void)do_write(eap);
- }
-
- /*
- * write current buffer to file 'eap->arg'
- * if 'eap->append' is TRUE, append to the file
- *
- * if *eap->arg == NUL write to current file
- *
- * return FAIL for failure, OK otherwise
- */
- int
- do_write(eap)
- exarg_T *eap;
- {
- int other;
- char_u *fname = NULL; /* init to shut up gcc */
- char_u *ffname;
- int retval = FAIL;
- char_u *free_fname = NULL;
- #ifdef FEAT_BROWSE
- char_u *browse_file = NULL;
- #endif
- buf_T *alt_buf = NULL;
-
- if (not_writing()) /* check 'write' option */
- return FAIL;
-
- ffname = eap->arg;
- #ifdef FEAT_BROWSE
- if (cmdmod.browse)
- {
- browse_file = do_browse(TRUE, (char_u *)_("Save As"), NULL,
- NULL, ffname, NULL, curbuf);
- if (browse_file == NULL)
- goto theend;
- ffname = browse_file;
- }
- #endif
- if (*ffname == NUL)
- {
- if (eap->cmdidx == CMD_saveas)
- {
- EMSG(_(e_argreq));
- goto theend;
- }
- other = FALSE;
- }
- else
- {
- fname = ffname;
- free_fname = fix_fname(ffname);
- /*
- * When out-of-memory, keep unexpanded file name, because we MUST be
- * able to write the file in this situation.
- */
- if (free_fname != NULL)
- ffname = free_fname;
- other = otherfile(ffname);
- }
-
- /*
- * If we have a new file, put its name in the list of alternate file names.
- */
- if (other)
- {
- if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL
- || eap->cmdidx == CMD_saveas)
- alt_buf = setaltfname(ffname, fname, (linenr_T)1);
- else
- alt_buf = buflist_findname(ffname);
- if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL)
- {
- /* Overwriting a file that is loaded in another buffer is not a
- * good idea. */
- EMSG(_("E139: File is loaded in another buffer"));
- goto theend;
- }
- }
-
- /*
- * Writing to the current file is not allowed in readonly mode
- * and a file name is required.
- * "nofile" and "nowrite" buffers cannot be written implicitly either.
- */
- if (!other && (
- #ifdef FEAT_QUICKFIX
- bt_dontwrite_msg(curbuf) ||
- #endif
- check_fname() == FAIL || check_readonly(&eap->forceit, curbuf)))
- goto theend;
-
- if (!other)
- {
- ffname = curbuf->b_ffname;
- fname = curbuf->b_fname;
- /*
- * Not writing the whole file is only allowed with '!'.
- */
- if ( (eap->line1 != 1
- || eap->line2 != curbuf->b_ml.ml_line_count)
- && !eap->forceit
- && !eap->append
- && !p_wa)
- {
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if (p_confirm || cmdmod.confirm)
- {
- if (vim_dialog_yesno(VIM_QUESTION, NULL,
- (char_u *)_("Write partial file?"), 2) != VIM_YES)
- goto theend;
- eap->forceit = TRUE;
- }
- else
- #endif
- {
- EMSG(_("E140: Use ! to write partial buffer"));
- goto theend;
- }
- }
- }
-
- if (eap->cmdidx == CMD_saveas && alt_buf != NULL)
- {
- /* Exchange the file names for the current and the alternate buffer.
- * This makes it look like we are now editing the buffer under the new
- * name. Must be done before buf_write(), because if there is no file
- * name and 'cpo' contains 'F', it will set the file name. */
- fname = alt_buf->b_fname;
- alt_buf->b_fname = curbuf->b_fname;
- curbuf->b_fname = fname;
- fname = alt_buf->b_ffname;
- alt_buf->b_ffname = curbuf->b_ffname;
- curbuf->b_ffname = fname;
- fname = alt_buf->b_sfname;
- alt_buf->b_sfname = curbuf->b_sfname;
- curbuf->b_sfname = fname;
- buf_name_changed();
- #ifdef FEAT_AUTOCMD
- if (!alt_buf->b_p_bl)
- {
- alt_buf->b_p_bl = TRUE;
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
- }
- #endif
- }
-
- if (check_overwrite(eap, curbuf, fname, ffname, other) == OK)
- retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
- eap, eap->append, eap->forceit, TRUE, FALSE);
-
- theend:
- #ifdef FEAT_BROWSE
- vim_free(browse_file);
- #endif
- vim_free(free_fname);
- return retval;
- }
-
- /*
- * Check if it is allowed to overwrite a file. If b_flags has BF_NOTEDITED,
- * BF_NEW or BF_READERR, check for overwriting current file.
- * May set eap->forceit if a dialog says it's OK to overwrite.
- * Return OK if it's OK, FAIL if it is not.
- */
- /*ARGSUSED*/
- static int
- check_overwrite(eap, buf, fname, ffname, other)
- exarg_T *eap;
- buf_T *buf;
- char_u *fname; /* file name to be used (can differ from
- buf->ffname) */
- char_u *ffname; /* full path version of fname */
- int other; /* writing under other name */
- {
- /*
- * write to other file or b_flags set or not writing the whole file:
- * overwriting only allowed with '!'
- */
- if ( (other
- || (buf->b_flags & BF_NOTEDITED)
- || ((buf->b_flags & BF_NEW)
- && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
- || (buf->b_flags & BF_READERR))
- && !eap->forceit
- && !eap->append
- && !p_wa
- && vim_fexists(ffname))
- {
- #ifdef UNIX
- /* with UNIX it is possible to open a directory */
- if (mch_isdir(ffname))
- {
- EMSG2(_(e_isadir2), ffname);
- return FAIL;
- }
- #endif
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if (p_confirm || cmdmod.confirm)
- {
- char_u buff[IOSIZE];
-
- dialog_msg(buff, _("Overwrite existing file \"%.*s\"?"), fname);
- if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES)
- return FAIL;
- eap->forceit = TRUE;
- }
- else
- #endif
- {
- EMSG(_(e_exists));
- return FAIL;
- }
- }
- return OK;
- }
-
- /*
- * Handle ":wnext", ":wNext" and ":wprevious" commands.
- */
- void
- ex_wnext(eap)
- exarg_T *eap;
- {
- int i;
-
- if (eap->cmd[1] == 'n')
- i = curwin->w_arg_idx + (int)eap->line2;
- else
- i = curwin->w_arg_idx - (int)eap->line2;
- eap->line1 = 1;
- eap->line2 = curbuf->b_ml.ml_line_count;
- if (do_write(eap) != FAIL)
- do_argfile(eap, i);
- }
-
- /*
- * ":wall", ":wqall" and ":xall": Write all changed files (and exit).
- */
- void
- do_wqall(eap)
- exarg_T *eap;
- {
- buf_T *buf;
- int error = 0;
-
- if (eap->cmdidx == CMD_xall || eap->cmdidx == CMD_wqall)
- exiting = TRUE;
-
- for (buf = firstbuf; buf != NULL; buf = buf->b_next)
- {
- if (bufIsChanged(buf))
- {
- /*
- * Check if there is a reason the buffer cannot be written:
- * 1. if the 'write' option is set
- * 2. if there is no file name (even after browsing)
- * 3. if the 'readonly' is set (even after a dialog)
- * 4. if overwriting is allowed (even after a dialog)
- */
- if (not_writing())
- {
- ++error;
- break;
- }
- #ifdef FEAT_BROWSE
- /* ":browse wall": ask for file name if there isn't one */
- if (buf->b_ffname == NULL && cmdmod.browse)
- buf->b_ffname = do_browse(TRUE, (char_u *)_("Save As"), NULL,
- NULL, (char_u *)"", NULL, buf);
- #endif
- if (buf->b_ffname == NULL)
- {
- EMSGN(_("E141: No file name for buffer %ld"), (long)buf->b_fnum);
- ++error;
- }
- else if (check_readonly(&eap->forceit, buf)
- || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname,
- FALSE) == FAIL)
- {
- ++error;
- }
- else
- {
- if (buf_write_all(buf, eap->forceit) == FAIL)
- ++error;
- #ifdef FEAT_AUTOCMD
- /* an autocommand may have deleted the buffer */
- if (!buf_valid(buf))
- buf = firstbuf;
- #endif
- }
- }
- }
- if (exiting)
- {
- if (!error)
- getout(0); /* exit Vim */
- not_exiting();
- }
- }
-
- /*
- * Check the 'write' option.
- * Return TRUE and give a message when it's not st.
- */
- int
- not_writing()
- {
- if (p_write)
- return FALSE;
- EMSG(_("E142: File not written: Writing is disabled by 'write' option"));
- return TRUE;
- }
-
- /*
- * Check if a buffer is read-only. Ask for overruling in a dialog.
- * Return TRUE and give an error message when the buffer is readonly.
- */
- static int
- check_readonly(forceit, buf)
- int *forceit;
- buf_T *buf;
- {
- if (!*forceit && buf->b_p_ro)
- {
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL)
- {
- char_u buff[IOSIZE];
-
- dialog_msg(buff, _("'readonly' option is set for \"%.*s\".\nDo you wish to write anyway?"),
- buf->b_fname);
-
- if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES)
- {
- /* Set forceit, to force the writing of a readonly file */
- *forceit = TRUE;
- return FALSE;
- }
- else
- return TRUE;
- }
- else
- #endif
- EMSG(_(e_readonly));
- return TRUE;
- }
- return FALSE;
- }
-
- /*
- * try to abandon current file and edit a new or existing file
- * 'fnum' is the number of the file, if zero use ffname/sfname
- *
- * return 1 for "normal" error, 2 for "not written" error, 0 for success
- * -1 for succesfully opening another file
- * 'lnum' is the line number for the cursor in the new file (if non-zero).
- */
- int
- getfile(fnum, ffname, sfname, setpm, lnum, forceit)
- int fnum;
- char_u *ffname;
- char_u *sfname;
- int setpm;
- linenr_T lnum;
- int forceit;
- {
- int other;
- int retval;
- char_u *free_me = NULL;
-
- #ifdef FEAT_CMDWIN
- if (cmdwin_type != 0)
- return 1;
- #endif
-
- if (fnum == 0)
- {
- fname_expand(&ffname, &sfname); /* make ffname full path, set sfname */
- other = otherfile(ffname);
- free_me = ffname; /* has been allocated, free() later */
- }
- else
- other = (fnum != curbuf->b_fnum);
-
- if (other)
- ++no_wait_return; /* don't wait for autowrite message */
- if (other && !forceit && curbuf->b_nwindows == 1 && !P_HID(curbuf)
- && curbufIsChanged() && autowrite(curbuf, forceit) == FAIL)
- {
- if (other)
- --no_wait_return;
- EMSG(_(e_nowrtmsg));
- retval = 2; /* file has been changed */
- goto theend;
- }
- if (other)
- --no_wait_return;
- if (setpm)
- setpcmark();
- if (!other)
- {
- if (lnum != 0)
- curwin->w_cursor.lnum = lnum;
- check_cursor_lnum();
- beginline(BL_SOL | BL_FIX);
- retval = 0; /* it's in the same file */
- }
- else if (do_ecmd(fnum, ffname, sfname, NULL, lnum,
- (P_HID(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0)) == OK)
- retval = -1; /* opened another file */
- else
- retval = 1; /* error encountered */
-
- theend:
- vim_free(free_me);
- return retval;
- }
-
- /*
- * start editing a new file
- *
- * fnum: file number; if zero use ffname/sfname
- * ffname: the file name
- * - full path if sfname used,
- * - any file name if sfname is NULL
- * - empty string to re-edit with the same file name (but may be
- * in a different directory)
- * - NULL to start an empty buffer
- * sfname: the short file name (or NULL)
- * eap: contains the command to be executed after loading the file and
- * forced 'ff' and 'fenc'
- * newlnum: if > 0: put cursor on this line number (if possible)
- * if ECMD_LASTL: use last position in loaded file
- * if ECMD_LAST: use last position in all files
- * if ECMD_ONE: use first line
- * flags:
- * ECMD_HIDE: if TRUE don't free the current buffer
- * ECMD_SET_HELP: set b_help flag of (new) buffer before opening file
- * ECMD_OLDBUF: use existing buffer if it exists
- * ECMD_FORCEIT: ! used for Ex command
- * ECMD_ADDBUF: don't edit, just add to buffer list
- *
- * return FAIL for failure, OK otherwise
- */
- int
- do_ecmd(fnum, ffname, sfname, eap, newlnum, flags)
- int fnum;
- char_u *ffname;
- char_u *sfname;
- exarg_T *eap; /* can be NULL! */
- linenr_T newlnum;
- int flags;
- {
- int other_file; /* TRUE if editing another file */
- int oldbuf; /* TRUE if using existing buffer */
- #ifdef FEAT_AUTOCMD
- int auto_buf = FALSE; /* TRUE if autocommands brought us
- into the buffer unexpectedly */
- char_u *new_name = NULL;
- #endif
- buf_T *buf;
- #if defined(FEAT_AUTOCMD) || defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- buf_T *old_curbuf = curbuf;
- #endif
- char_u *free_fname = NULL;
- #ifdef FEAT_BROWSE
- char_u *browse_file = NULL;
- #endif
- int retval = FAIL;
- long n;
- linenr_T lnum;
- linenr_T topline = 0;
- int newcol = -1;
- int solcol = -1;
- pos_T *pos;
- #ifdef FEAT_SUN_WORKSHOP
- char_u *cp;
- #endif
- char_u *command = NULL;
-
- if (eap != NULL)
- command = eap->do_ecmd_cmd;
-
- if (fnum != 0)
- {
- if (fnum == curbuf->b_fnum) /* file is already being edited */
- return OK; /* nothing to do */
- other_file = TRUE;
- }
- else
- {
- #ifdef FEAT_BROWSE
- if (cmdmod.browse)
- {
- browse_file = do_browse(FALSE, (char_u *)_("Edit File"), NULL,
- NULL, ffname, NULL, curbuf);
- if (browse_file == NULL)
- goto theend;
- ffname = browse_file;
- }
- #endif
- /* if no short name given, use ffname for short name */
- if (sfname == NULL)
- sfname = ffname;
- #ifdef USE_FNAME_CASE
- # ifdef USE_LONG_FNAME
- if (USE_LONG_FNAME)
- # endif
- fname_case(sfname, 0); /* set correct case for short file name */
- #endif
-
- #ifdef FEAT_LISTCMDS
- if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL))
- goto theend;
- #endif
-
- if (ffname == NULL)
- other_file = TRUE;
- /* there is no file name */
- else if (*ffname == NUL && curbuf->b_ffname == NULL)
- other_file = FALSE;
- else
- {
- if (*ffname == NUL) /* re-edit with same file name */
- {
- ffname = curbuf->b_ffname;
- sfname = curbuf->b_fname;
- }
- free_fname = fix_fname(ffname); /* may expand to full path name */
- if (free_fname != NULL)
- ffname = free_fname;
- other_file = otherfile(ffname);
- #ifdef FEAT_SUN_WORKSHOP
- if (usingSunWorkShop && (cp = vim_strrchr(sfname, '/')) != NULL)
- sfname = ++cp;
- #endif
- }
- }
-
- /*
- * if the file was changed we may not be allowed to abandon it
- * - if we are going to re-edit the same file
- * - or if we are the only window on this file and if ECMD_HIDE is FALSE
- */
- if ( ((!other_file && !(flags & ECMD_OLDBUF))
- || (curbuf->b_nwindows == 1
- && !(flags & (ECMD_HIDE | ECMD_ADDBUF))))
- && check_changed(curbuf, p_awa, !other_file,
- (flags & ECMD_FORCEIT), FALSE))
- {
- if (fnum == 0 && other_file && ffname != NULL)
- (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum);
- goto theend;
- }
-
- #ifdef FEAT_VISUAL
- /*
- * End Visual mode before switching to another buffer, so the text can be
- * copied into the GUI selection buffer.
- */
- reset_VIsual();
- #endif
-
- /*
- * If we are starting to edit another file, open a (new) buffer.
- * Otherwise we re-use the current buffer.
- */
- if (other_file)
- {
- #ifdef FEAT_LISTCMDS
- if (!(flags & ECMD_ADDBUF))
- #endif
- {
- curwin->w_alt_fnum = curbuf->b_fnum;
- buflist_altfpos();
- }
-
- if (fnum)
- buf = buflist_findnr(fnum);
- else
- {
- #ifdef FEAT_LISTCMDS
- if (flags & ECMD_ADDBUF)
- {
- linenr_T tlnum = 1L;
-
- if (command != NULL)
- {
- tlnum = atol((char *)command);
- if (tlnum <= 0)
- tlnum = 1L;
- }
- (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED);
- goto theend;
- }
- #endif
- buf = buflist_new(ffname, sfname, 0L,
- BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED));
- }
- if (buf == NULL)
- goto theend;
- if (buf->b_ml.ml_mfp == NULL) /* no memfile yet */
- {
- oldbuf = FALSE;
- buf->b_nwindows = 0;
- }
- else /* existing memfile */
- {
- oldbuf = TRUE;
- (void)buf_check_timestamp(buf, FALSE);
- /* Check if autocommands made buffer invalid. */
- if (!buf_valid(buf))
- goto theend;
- }
-
- /* May jump to last used line number for a loaded buffer or when asked
- * for explicitly */
- if ((oldbuf && newlnum == ECMD_LASTL) || newlnum == ECMD_LAST)
- {
- pos = buflist_findfpos(buf);
- newlnum = pos->lnum;
- solcol = pos->col;
- }
-
- /*
- * Make the (new) buffer the one used by the current window.
- * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE.
- * If the current buffer was empty and has no file name, curbuf
- * is returned by buflist_new().
- */
- if (buf != curbuf)
- {
- #ifdef FEAT_AUTOCMD
- /*
- * Be careful: The autocommands may delete any buffer and change
- * the current buffer.
- * - If the buffer we are going to edit is deleted, give up.
- * - If the current buffer is deleted, prefer to load the new
- * buffer when loading a buffer is required. This avoids
- * loading another buffer which then must be closed again.
- * - If we ended up in the new buffer already, need to skip a few
- * things, set auto_buf.
- */
- if (buf->b_fname != NULL)
- new_name = vim_strsave(buf->b_fname);
- au_new_curbuf = buf;
- apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
- if (!buf_valid(buf)) /* new buffer has been deleted */
- {
- delbuf_msg(new_name); /* frees new_name */
- goto theend;
- }
- if (buf == curbuf) /* already in new buffer */
- auto_buf = TRUE;
- else
- {
- if (curbuf == old_curbuf)
- #endif
- buf_copy_options(buf, BCO_ENTER);
-
- /* close the link to the current buffer */
- close_buffer(curwin, curbuf,
- (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD);
-
- #ifdef FEAT_AUTOCMD
- /* Be careful again, like above. */
- if (!buf_valid(buf)) /* new buffer has been deleted */
- {
- delbuf_msg(new_name); /* frees new_name */
- goto theend;
- }
- if (buf == curbuf) /* already in new buffer */
- auto_buf = TRUE;
- else
- #endif
-
- {
- curwin->w_buffer = buf;
- curbuf = buf;
- ++curbuf->b_nwindows;
- /* set 'fileformat' */
- if (*p_ffs && !oldbuf)
- set_fileformat(default_fileformat(), OPT_LOCAL);
- }
-
- /* May get the window options from the last time this buffer
- * was in this window (or another window). If not used
- * before, reset the local window options to the global
- * values. Also restores old folding stuff. */
- get_winopts(buf);
-
- #ifdef FEAT_AUTOCMD
- }
- vim_free(new_name);
- au_new_curbuf = NULL;
- #endif
- }
- else
- ++curbuf->b_nwindows;
-
- curwin->w_pcmark.lnum = 1;
- curwin->w_pcmark.col = 0;
- }
- else /* !other_file */
- {
- if (
- #ifdef FEAT_LISTCMDS
- (flags & ECMD_ADDBUF) ||
- #endif
- check_fname() == FAIL)
- goto theend;
- oldbuf = (flags & ECMD_OLDBUF);
- }
-
- if ((flags & ECMD_SET_HELP) || keep_help_flag)
- {
- curbuf->b_help = TRUE;
- #ifdef FEAT_QUICKFIX
- set_string_option_direct((char_u *)"buftype", -1,
- (char_u *)"help", OPT_FREE |OPT_LOCAL);
- #endif
- curbuf->b_p_ma = FALSE;
- curbuf->b_p_bin = FALSE; /* reset 'bin' before reading file */
- curwin->w_p_nu = 0; /* no line numbers */
- #ifdef FEAT_RIGHTLEFT
- curwin->w_p_rl = 0; /* help window is left-to-right */
- #endif
- #ifdef FEAT_FOLDING
- curwin->w_p_fen = FALSE; /* No folding in the help window */
- #endif
- #ifdef FEAT_DIFF
- curwin->w_p_diff = FALSE; /* No 'diff' */
- #endif
-
- #ifdef FEAT_AUTOCMD
- buf = curbuf;
- #endif
- set_buflisted(FALSE);
- }
- else
- {
- #ifdef FEAT_AUTOCMD
- buf = curbuf;
- #endif
- set_buflisted(TRUE);
- }
-
- #ifdef FEAT_AUTOCMD
- /* If autocommands change buffers under our fingers, forget about
- * editing the file. */
- if (buf != curbuf)
- goto theend;
-
- /* Since we are starting to edit a file, consider the filetype to be
- * unset. Helps for when an autocommand changes files and expects syntax
- * highlighting to work in the other file. */
- did_filetype = FALSE;
- #endif
-
- /*
- * other_file oldbuf
- * FALSE FALSE re-edit same file, buffer is re-used
- * FALSE TRUE re-edit same file, nothing changes
- * TRUE FALSE start editing new file, new buffer
- * TRUE TRUE start editing in existing buffer (nothing to do)
- */
- if (!other_file && !oldbuf) /* re-use the buffer */
- {
- set_last_cursor(curwin); /* may set b_last_cursor */
- if (newlnum == ECMD_LAST || newlnum == ECMD_LASTL)
- {
- newlnum = curwin->w_cursor.lnum;
- solcol = curwin->w_cursor.col;
- }
- #ifdef FEAT_AUTOCMD
- buf = curbuf;
- if (buf->b_fname != NULL)
- new_name = vim_strsave(buf->b_fname);
- else
- new_name = NULL;
- #endif
- buf_freeall(curbuf, FALSE); /* free all things for buffer */
- #ifdef FEAT_AUTOCMD
- /* If autocommands deleted the buffer we were going to re-edit, give
- * up and jump to the end. */
- if (!buf_valid(buf))
- {
- delbuf_msg(new_name); /* frees new_name */
- goto theend;
- }
- vim_free(new_name);
-
- /* If autocommands change buffers under our fingers, forget about
- * re-editing the file. Should do the buf_clear_file(), but perhaps
- * the autocommands changed the buffer... */
- if (buf != curbuf)
- goto theend;
- #endif
- buf_clear_file(curbuf);
- curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */
- curbuf->b_op_end.lnum = 0;
- }
-
- /*
- * If we get here we are sure to start editing
- */
- /* don't redraw until the cursor is in the right line */
- ++RedrawingDisabled;
-
- /* Assume success now */
- retval = OK;
-
- /*
- * Reset cursor position, could be used by autocommands.
- */
- check_cursor();
-
- /*
- * Check if we are editing the w_arg_idx file in the argument list.
- */
- check_arg_idx(curwin);
-
- #ifdef FEAT_AUTOCMD
- if (!auto_buf)
- #endif
- {
- /*
- * Set cursor and init window before reading the file and executing
- * autocommands. This allows for the autocommands to position the
- * cursor.
- */
- win_init(curwin);
-
- #ifdef FEAT_FOLDING
- /* It's like all lines in the buffer changed. Need to update
- * automatic folding. */
- foldUpdateAll(curwin);
- #endif
-
- #ifdef FEAT_SUN_WORKSHOP
- if (usingSunWorkShop && curbuf->b_ffname != NULL
- && vim_chdirfile(curbuf->b_ffname) == OK)
- shorten_fnames(TRUE);
- #endif
- /*
- * Careful: open_buffer() and apply_autocmds() may change the current
- * buffer and window.
- */
- lnum = curwin->w_cursor.lnum;
- topline = curwin->w_topline;
- if (!oldbuf) /* need to read the file */
- {
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- swap_exists_action = SEA_DIALOG;
- #endif
- curbuf->b_flags |= BF_CHECK_RO; /* set/reset 'ro' flag */
-
- /*
- * Open the buffer and read the file.
- */
- (void)open_buffer(FALSE, eap);
-
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if (swap_exists_action == SEA_QUIT)
- retval = FAIL;
- handle_swap_exists(old_curbuf);
- #endif
- }
- #ifdef FEAT_AUTOCMD
- else
- {
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
- }
- check_arg_idx(curwin);
- #endif
-
- /*
- * If autocommands change the cursor position or topline, we should
- * keep it.
- */
- if (curwin->w_cursor.lnum != lnum)
- {
- newlnum = curwin->w_cursor.lnum;
- newcol = curwin->w_cursor.col;
- }
- if (curwin->w_topline == topline)
- topline = 0;
-
- /* Even when cursor didn't move we need to recompute topline. */
- changed_line_abv_curs();
-
- #ifdef FEAT_TITLE
- maketitle();
- #endif
- }
-
- #ifdef FEAT_DIFF
- /* Tell the diff stuff that this buffer is new and/or needs updating.
- * Also needed when re-editing the same buffer, because unloading will
- * have removed it as a diff buffer. */
- diff_new_buffer();
- diff_invalidate();
- #endif
-
- if (command == NULL)
- {
- if (newcol >= 0) /* position set by autocommands */
- {
- curwin->w_cursor.lnum = newlnum;
- curwin->w_cursor.col = newcol;
- check_cursor();
- }
- else if (newlnum > 0) /* line number from caller or old position */
- {
- curwin->w_cursor.lnum = newlnum;
- check_cursor_lnum();
- if (solcol >= 0 && !p_sol)
- {
- /* 'sol' is off: Use last known column. */
- curwin->w_cursor.col = solcol;
- check_cursor_col();
- #ifdef FEAT_VIRTUALEDIT
- curwin->w_cursor.coladd = 0;
- #endif
- curwin->w_set_curswant = TRUE;
- }
- else
- beginline(BL_SOL | BL_FIX);
- }
- else /* no line number, go to last line in Ex mode */
- {
- if (exmode_active)
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- beginline(BL_WHITE | BL_FIX);
- }
- }
-
- #ifdef FEAT_WINDOWS
- /* Check if cursors in other windows on the same buffer are still valid */
- check_lnums(FALSE);
- #endif
-
- /*
- * Did not read the file, need to show some info about the file.
- * Do this after setting the cursor.
- */
- if (oldbuf
- #ifdef FEAT_AUTOCMD
- && !auto_buf
- #endif
- )
- fileinfo(FALSE, TRUE, FALSE);
-
- if (command != NULL)
- do_cmdline(command, NULL, NULL, DOCMD_VERBOSE);
-
- #ifdef FEAT_KEYMAP
- if (curbuf->b_kmap_state & KEYMAP_INIT)
- keymap_init();
- #endif
-
- --RedrawingDisabled;
- if (!skip_redraw)
- {
- n = p_so;
- if (topline == 0 && command == NULL)
- p_so = 999; /* force cursor halfway the window */
- update_topline();
- #ifdef FEAT_SCROLLBIND
- curwin->w_scbind_pos = curwin->w_topline;
- #endif
- p_so = n;
- redraw_curbuf_later(NOT_VALID); /* redraw this buffer later */
- }
-
- if (p_im)
- need_start_insertmode = TRUE;
-
- #ifdef FEAT_SUN_WORKSHOP
- if (usingSunWorkShop && curbuf->b_ffname != NULL
- && vim_chdirfile(curbuf->b_ffname) == OK)
- shorten_fnames(TRUE);
-
- if (gui.in_use && curbuf != NULL && curbuf->b_fname != NULL)
- workshop_file_opened((char *)curbuf->b_ffname, curbuf->b_p_ro);
- #endif
-
- theend:
- #ifdef FEAT_BROWSE
- vim_free(browse_file);
- #endif
- vim_free(free_fname);
- return retval;
- }
-
- #ifdef FEAT_AUTOCMD
- static void
- delbuf_msg(name)
- char_u *name;
- {
- EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"),
- name == NULL ? (char_u *)"" : name);
- vim_free(name);
- au_new_curbuf = NULL;
- }
- #endif
-
- /*
- * ":insert" and ":append", also used by ":change"
- */
- void
- ex_append(eap)
- exarg_T *eap;
- {
- char_u *theline;
- int did_undo = FALSE;
- linenr_T lnum = eap->line2;
-
- if (eap->cmdidx != CMD_append)
- --lnum;
-
- State = INSERT; /* behave like in Insert mode */
- if (curbuf->b_p_iminsert == B_IMODE_LMAP)
- State |= LANGMAP;
- while (1)
- {
- msg_scroll = TRUE;
- need_wait_return = FALSE;
- if (eap->getline == NULL)
- theline = getcmdline(
- #ifdef FEAT_EVAL
- eap->cstack->cs_whilelevel > 0 ? -1 :
- #endif
- NUL, 0L, 0);
- else
- theline = eap->getline(
- #ifdef FEAT_EVAL
- eap->cstack->cs_whilelevel > 0 ? -1 :
- #endif
- NUL, eap->cookie, 0);
- lines_left = Rows - 1;
- if (theline == NULL || (theline[0] == '.' && theline[1] == NUL))
- break;
-
- if (!did_undo && u_save(lnum, lnum + 1) == FAIL)
- break;
- did_undo = TRUE;
- ml_append(lnum, theline, (colnr_T)0, FALSE);
- appended_lines_mark(lnum, 1L);
-
- vim_free(theline);
- ++lnum;
- msg_didout = TRUE; /* also scroll for empty line */
- }
- State = NORMAL;
-
- /* "start" is set to eap->line2+1 unless that position is invalid (when
- * eap->line2 pointed to the end of the buffer and nothig was appended)
- * "end" is set to lnum when something has been appended, otherwise
- * it is the same than "start" -- Acevedo */
- curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ?
- eap->line2 + 1 : curbuf->b_ml.ml_line_count;
- if (eap->cmdidx != CMD_append)
- --curbuf->b_op_start.lnum;
- curbuf->b_op_end.lnum = (eap->line2 < lnum)
- ? lnum : curbuf->b_op_start.lnum;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
- curwin->w_cursor.lnum = lnum;
- check_cursor_lnum();
- beginline(BL_SOL | BL_FIX);
-
- need_wait_return = FALSE; /* don't use wait_return() now */
- ex_no_reprint = TRUE;
- }
-
- /*
- * ":change"
- */
- void
- ex_change(eap)
- exarg_T *eap;
- {
- linenr_T lnum;
-
- if (eap->line2 >= eap->line1
- && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL)
- return;
-
- for (lnum = eap->line2; lnum >= eap->line1; --lnum)
- {
- if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
- break;
- ml_delete(eap->line1, FALSE);
- }
- deleted_lines_mark(eap->line1, (long)(eap->line2 - lnum));
-
- /* ":append" on the line above the deleted lines. */
- eap->line2 = eap->line1;
- ex_append(eap);
- }
-
- void
- ex_z(eap)
- exarg_T *eap;
- {
- char_u *x;
- int bigness = curwin->w_height - 3;
- char_u kind;
- int numbered = FALSE;
- int minus = 0;
- linenr_T start, end, curs, i;
- int j;
- linenr_T lnum = eap->line2;
-
- if (bigness < 1)
- bigness = 1;
-
- x = eap->arg;
- if (*x == '#')
- {
- numbered = TRUE;
- ++x;
- }
-
- kind = *x;
- if (kind == '-' || kind == '+' || kind == '=' || kind == '^' || kind == '.')
- ++x;
-
- if (*x != 0)
- {
- if (!isdigit(*x))
- {
- EMSG(_("E144: non-numeric argument to :z"));
- return;
- }
- else
- bigness = atoi((char *)x);
- }
-
- switch (kind)
- {
- case '-':
- start = lnum - bigness;
- end = lnum;
- curs = lnum;
- break;
-
- case '=':
- start = lnum - bigness / 2 + 1;
- end = lnum + bigness / 2 - 1;
- curs = lnum;
- minus = 1;
- break;
-
- case '^':
- start = lnum - bigness * 2;
- end = lnum - bigness;
- curs = lnum - bigness;
- break;
-
- case '.':
- start = lnum - bigness / 2;
- end = lnum + bigness / 2;
- curs = end;
- break;
-
- default: /* '+' */
- start = lnum;
- end = lnum + bigness;
- curs = end;
- break;
- }
-
- if (start < 1)
- start = 1;
-
- if (end > curbuf->b_ml.ml_line_count)
- end = curbuf->b_ml.ml_line_count;
-
- if (curs > curbuf->b_ml.ml_line_count)
- curs = curbuf->b_ml.ml_line_count;
-
- for (i = start; i <= end; i++)
- {
- if (minus && i == lnum)
- {
- msg_putchar('\n');
-
- for (j = 1; j < Columns; j++)
- msg_putchar('-');
- }
-
- print_line(i, numbered);
-
- if (minus && i == lnum)
- {
- msg_putchar('\n');
-
- for (j = 1; j < Columns; j++)
- msg_putchar('-');
- }
- }
-
- curwin->w_cursor.lnum = curs;
- ex_no_reprint = TRUE;
- }
-
- /*
- * Check if the restricted flag is set.
- * If so, give an error message and return TRUE.
- * Otherwise, return FALSE.
- */
- int
- check_restricted()
- {
- if (restricted)
- {
- EMSG(_("E145: Shell commands not allowed in rvim"));
- return TRUE;
- }
- return FALSE;
- }
-
- /*
- * Check if the secure flag is set (.exrc or .vimrc in current directory).
- * If so, give an error message and return TRUE.
- * Otherwise, return FALSE.
- */
- int
- check_secure()
- {
- if (secure)
- {
- secure = 2;
- EMSG(_(e_curdir));
- return TRUE;
- }
- #ifdef HAVE_SANDBOX
- /*
- * In the sandbox more things are not allowed, including the things
- * disallowed in secure mode.
- */
- if (sandbox != 0)
- {
- EMSG(_(e_sandbox));
- return TRUE;
- }
- #endif
- return FALSE;
- }
-
- static char_u *old_sub = NULL; /* previous substitute pattern */
- static int global_need_beginline; /* call beginline() after ":g" */
-
- /*
- * When ":global" is used to number of substitutions and changed lines is
- * accumulated until it's finished.
- */
- static long sub_nsubs; /* total number of substitutions */
- static linenr_T sub_nlines; /* total number of lines changed */
-
- /* do_sub()
- *
- * Perform a substitution from line eap->line1 to line eap->line2 using the
- * command pointed to by eap->arg which should be of the form:
- *
- * /pattern/substitution/{flags}
- *
- * The usual escapes are supported as described in the regexp docs.
- */
- void
- do_sub(eap)
- exarg_T *eap;
- {
- linenr_T lnum;
- long i = 0;
- regmmatch_T regmatch;
- static int do_all = FALSE; /* do multiple substitutions per line */
- static int do_ask = FALSE; /* ask for confirmation */
- static int do_error = TRUE; /* if false, ignore errors */
- static int do_print = FALSE; /* print last line with subs. */
- static int do_ic = 0; /* ignore case flag */
- char_u *pat = NULL, *sub = NULL; /* init for GCC */
- int delimiter;
- int sublen;
- int got_quit = FALSE;
- int got_match = FALSE;
- int temp;
- int which_pat;
- char_u *cmd;
- int save_State;
- linenr_T first_line = 0; /* first changed line */
- linenr_T last_line= 0; /* below last changed line AFTER the
- * change */
- linenr_T old_line_count = curbuf->b_ml.ml_line_count;
- linenr_T line2;
- long nmatch; /* number of lines in match */
- linenr_T sub_firstlnum; /* nr of first sub line */
- char_u *sub_firstline; /* allocated copy of first sub line */
-
- cmd = eap->arg;
- if (!global_busy)
- {
- sub_nsubs = 0;
- sub_nlines = 0;
- }
-
- #ifdef FEAT_FKMAP /* reverse the flow of the Farsi characters */
- if (p_altkeymap && curwin->w_p_rl)
- lrF_sub(cmd);
- #endif
-
- if (eap->cmdidx == CMD_tilde)
- which_pat = RE_LAST; /* use last used regexp */
- else
- which_pat = RE_SUBST; /* use last substitute regexp */
-
- /* new pattern and substitution */
- if (eap->cmd[0] == 's' && *cmd != NUL && !vim_iswhite(*cmd)
- && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL)
- {
- /* don't accept alphanumeric for separator */
- if (isalpha(*cmd))
- {
- EMSG(_("E146: Regular expressions can't be delimited by letters"));
- return;
- }
- /*
- * undocumented vi feature:
- * "\/sub/" and "\?sub?" use last used search pattern (almost like
- * //sub/r). "\&sub&" use last substitute pattern (like //sub/).
- */
- if (*cmd == '\\')
- {
- ++cmd;
- if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
- {
- EMSG(_(e_backslash));
- return;
- }
- if (*cmd != '&')
- which_pat = RE_SEARCH; /* use last '/' pattern */
- pat = (char_u *)""; /* empty search pattern */
- delimiter = *cmd++; /* remember delimiter character */
- }
- else /* find the end of the regexp */
- {
- which_pat = RE_LAST; /* use last used regexp */
- delimiter = *cmd++; /* remember delimiter character */
- pat = cmd; /* remember start of search pat */
- cmd = skip_regexp(cmd, delimiter, p_magic);
- if (cmd[0] == delimiter) /* end delimiter found */
- *cmd++ = NUL; /* replace it with a NUL */
- }
-
- /*
- * Small incompatibility: vi sees '\n' as end of the command, but in
- * Vim we want to use '\n' to find/substitute a NUL.
- */
- sub = cmd; /* remember the start of the substitution */
-
- while (cmd[0])
- {
- if (cmd[0] == delimiter) /* end delimiter found */
- {
- *cmd++ = NUL; /* replace it with a NUL */
- break;
- }
- if (cmd[0] == '\\' && cmd[1] != 0) /* skip escaped characters */
- ++cmd;
- #ifdef FEAT_MBYTE
- if (has_mbyte)
- cmd += (*mb_ptr2len_check)(cmd);
- else
- #endif
- ++cmd;
- }
-
- if (!eap->skip)
- {
- vim_free(old_sub);
- old_sub = vim_strsave(sub);
- }
- }
- else if (!eap->skip) /* use previous pattern and substitution */
- {
- if (old_sub == NULL) /* there is no previous command */
- {
- EMSG(_(e_nopresub));
- return;
- }
- pat = NULL; /* search_regcomp() will use previous pattern */
- sub = old_sub;
- }
-
- /*
- * Find trailing options. When '&' is used, keep old options.
- */
- if (*cmd == '&')
- ++cmd;
- else
- {
- if (!p_ed)
- {
- if (p_gd) /* default is global on */
- do_all = TRUE;
- else
- do_all = FALSE;
- do_ask = FALSE;
- }
- do_error = TRUE;
- do_print = FALSE;
- do_ic = 0;
- }
- while (*cmd)
- {
- /*
- * Note that 'g' and 'c' are always inverted, also when p_ed is off.
- * 'r' is never inverted.
- */
- if (*cmd == 'g')
- do_all = !do_all;
- else if (*cmd == 'c')
- do_ask = !do_ask;
- else if (*cmd == 'e')
- do_error = !do_error;
- else if (*cmd == 'r') /* use last used regexp */
- which_pat = RE_LAST;
- else if (*cmd == 'p')
- do_print = TRUE;
- else if (*cmd == 'i') /* ignore case */
- do_ic = 'i';
- else if (*cmd == 'I') /* don't ignore case */
- do_ic = 'I';
- else
- break;
- ++cmd;
- }
-
- /*
- * check for a trailing count
- */
- cmd = skipwhite(cmd);
- if (isdigit(*cmd))
- {
- i = getdigits(&cmd);
- if (i <= 0 && !eap->skip && do_error)
- {
- EMSG(_(e_zerocount));
- return;
- }
- eap->line1 = eap->line2;
- eap->line2 += i - 1;
- }
-
- /*
- * check for trailing command or garbage
- */
- cmd = skipwhite(cmd);
- if (*cmd && *cmd != '"') /* if not end-of-line or comment */
- {
- eap->nextcmd = check_nextcmd(cmd);
- if (eap->nextcmd == NULL)
- {
- EMSG(_(e_trailing));
- return;
- }
- }
-
- if (eap->skip) /* not executing commands, only parsing */
- return;
-
- if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL)
- {
- if (do_error)
- EMSG(_(e_invcmd));
- return;
- }
-
- /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
- if (do_ic == 'i')
- regmatch.rmm_ic = TRUE;
- else if (do_ic == 'I')
- regmatch.rmm_ic = FALSE;
-
- sub_firstline = NULL;
-
- /*
- * ~ in the substitute pattern is replaced with the old pattern.
- * We do it here once to avoid it to be replaced over and over again.
- */
- sub = regtilde(sub, p_magic);
-
- /*
- * Check for a match on each line.
- */
- line2 = eap->line2;
- for (lnum = eap->line1; lnum <= line2 && !(got_int || got_quit); ++lnum)
- {
- sub_firstlnum = lnum;
- nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, (colnr_T)0);
- if (nmatch)
- {
- colnr_T copycol;
- colnr_T matchcol;
- colnr_T prev_matchcol = MAXCOL;
- char_u *new_end, *new_start = NULL;
- unsigned new_start_len = 0;
- char_u *p1;
- int did_sub = FALSE;
- int lastone;
- unsigned len, needed_len;
- long nmatch_tl = 0; /* nr of lines matched below lnum */
- int do_again; /* do it again after joining lines */
-
- /*
- * The new text is build up step by step, to avoid too much
- * copying. There are these pieces:
- * sub_firstline The old text, unmodifed.
- * copycol Column in the old text where we started
- * looking for a match; from here old text still
- * needs to be copied to the new text.
- * matchcol Column number of the old text where to look
- * for the next match. It's just after the
- * previous match or one further.
- * prev_matchcol Column just after the previous match (if any).
- * Mostly equal to matchcol, except for the first
- * match and after skipping an empty match.
- * regmatch.*pos Where the pattern matched in the old text.
- * new_start The new text, all that has been produced so
- * far.
- * new_end The new text, where to append new text.
- *
- * lnum The line number where we were looking for the
- * first match in the old line.
- * sub_firstlnum The line number in the buffer where to look
- * for a match. Can be different from "lnum"
- * when the pattern or substitute string contains
- * line breaks.
- *
- * Special situations:
- * - When the substitute string contains a line break, the part up
- * to the line break is inserted in the text, but the copy of
- * the original line is kept. "sub_firstlnum" is adjusted for
- * the inserted lines.
- * - When the matched pattern contains a line break, the old line
- * is taken from the line at the end of the pattern. The lines
- * in the match are deleted later, "sub_firstlnum" is adjusted
- * accordingly.
- *
- * The new text is built up in new_start[]. It has some extra
- * room to avoid using alloc()/free() too often. new_start_len is
- * the lenght of the allocated memory at new_start.
- *
- * Make a copy of the old line, so it won't be taken away when
- * updating the screen or handling a multi-line match. The "old_"
- * pointers point into this copy.
- */
- sub_firstline = vim_strsave(ml_get(sub_firstlnum));
- if (sub_firstline == NULL)
- {
- vim_free(new_start);
- goto outofmem;
- }
- copycol = 0;
- matchcol = 0;
-
- /* At first match, remember current cursor position. */
- if (!got_match)
- {
- setpcmark();
- got_match = TRUE;
- }
-
- /*
- * Loop until nothing more to replace in this line.
- * 1. Handle match with empty string.
- * 2. If do_ask is set, ask for confirmation.
- * 3. substitute the string.
- * 4. if do_all is set, find next match
- * 5. break if there isn't another match in this line
- */
- for (;;)
- {
- /* Save the line number of the last change for the final
- * cursor position (just like Vi). */
- curwin->w_cursor.lnum = lnum;
- do_again = FALSE;
-
- /*
- * 1. Match empty string does not count, except for first
- * match. This reproduces the strange vi behaviour.
- * This also catches endless loops.
- */
- if (matchcol == prev_matchcol
- && regmatch.endpos[0].lnum == 0
- && matchcol == regmatch.endpos[0].col)
- {
- ++matchcol; /* search for a match at next column */
- goto skip;
- }
-
- /* Normally we continue searching for a match just after the
- * previous match. */
- matchcol = regmatch.endpos[0].col;
- prev_matchcol = matchcol;
-
- /*
- * 2. If do_ask is set, ask for confirmation.
- */
- if (do_ask)
- {
- /* change State to CONFIRM, so that the mouse works
- * properly */
- save_State = State;
- State = CONFIRM;
- #ifdef FEAT_MOUSE
- setmouse(); /* disable mouse in xterm */
- #endif
- curwin->w_cursor.col = regmatch.startpos[0].col;
-
- /* When 'cpoptions' contains "u" don't sync undo when
- * asking for confirmation. */
- if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
- ++no_u_sync;
-
- /*
- * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
- */
- while (do_ask)
- {
- #ifdef FEAT_FOLDING
- int save_p_fen = curwin->w_p_fen;
-
- curwin->w_p_fen = FALSE;
- #endif
- /* Invert the matched string.
- * Remove the inversion afterwards. */
- temp = RedrawingDisabled;
- RedrawingDisabled = 0;
-
- search_match_lines = regmatch.endpos[0].lnum;
- search_match_endcol = regmatch.endpos[0].col;
- highlight_match = TRUE;
-
- update_topline();
- validate_cursor();
- update_screen(NOT_VALID);
- highlight_match = FALSE;
- redraw_later(NOT_VALID);
-
- #ifdef FEAT_FOLDING
- curwin->w_p_fen = save_p_fen;
- #endif
- if (msg_row == Rows - 1)
- msg_didout = FALSE; /* avoid a scroll-up */
- msg_starthere();
- i = msg_scroll;
- msg_scroll = 0; /* truncate msg when needed */
- msg_no_more = TRUE;
- /* write message same highlighting as for wait_return */
- smsg_attr(hl_attr(HLF_R),
- (char_u *)_("replace with %s (y/n/a/q/l/^E/^Y)?"),
- sub);
- msg_no_more = FALSE;
- msg_scroll = i;
- showruler(TRUE);
- windgoto(msg_row, msg_col);
- RedrawingDisabled = temp;
-
- #ifdef USE_ON_FLY_SCROLL
- dont_scroll = FALSE; /* allow scrolling here */
- #endif
- ++no_mapping; /* don't map this key */
- ++allow_keys; /* allow special keys */
- i = safe_vgetc();
- --allow_keys;
- --no_mapping;
-
- /* clear the question */
- msg_didout = FALSE; /* don't scroll up */
- msg_col = 0;
- gotocmdline(TRUE);
- need_wait_return = FALSE; /* no hit-return prompt */
- if (i == 'q' || i == ESC || i == Ctrl_C
- #ifdef UNIX
- || i == intr_char
- #endif
- )
- {
- got_quit = TRUE;
- break;
- }
- if (i == 'n')
- break;
- if (i == 'y')
- break;
- if (i == 'l')
- {
- /* last: replace and then stop */
- do_all = FALSE;
- line2 = lnum;
- break;
- }
- if (i == 'a')
- {
- do_ask = FALSE;
- break;
- }
- #ifdef FEAT_INS_EXPAND
- if (i == Ctrl_E)
- scrollup_clamp();
- else if (i == Ctrl_Y)
- scrolldown_clamp();
- #endif
- }
- State = save_State;
- #ifdef FEAT_MOUSE
- setmouse();
- #endif
- if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
- --no_u_sync;
-
- if (i == 'n')
- {
- /* For a multi-line match, put matchcol at the NUL at
- * the end of the line, so that we continue looking
- * for a match on the next line. Avoids that
- * ":s/\n//gc" get stuck. */
- if (nmatch > 1)
- matchcol = STRLEN(sub_firstline);
- goto skip;
- }
- if (got_quit)
- break;
- }
-
- /* Move the cursor to the start of the line, to avoid that it
- * is beyond the end of the line after the substitution. */
- curwin->w_cursor.col = 0;
-
- /*
- * 3. substitute the string.
- */
- /* get length of substitution part */
- sublen = vim_regsub_multi(®match, sub_firstlnum,
- sub, sub_firstline, FALSE, p_magic, TRUE);
-
- /* Need room for:
- * - result so far in new_start (not for first sub in line)
- * - original text up to match
- * - length of substituted part
- * - original text after match
- */
- if (nmatch == 1)
- p1 = sub_firstline;
- else
- {
- p1 = ml_get(sub_firstlnum + nmatch - 1);
- nmatch_tl += nmatch - 1;
- }
- i = regmatch.startpos[0].col - copycol;
- needed_len = i + ((unsigned)STRLEN(p1) - regmatch.endpos[0].col)
- + sublen + 1;
- if (new_start == NULL)
- {
- /*
- * Get some space for a temporary buffer to do the
- * substitution into (and some extra space to avoid
- * too many calls to alloc()/free()).
- */
- new_start_len = needed_len + 50;
- if ((new_start = alloc_check(new_start_len)) == NULL)
- goto outofmem;
- *new_start = NUL;
- new_end = new_start;
- }
- else
- {
- /*
- * Check if the temporary buffer is long enough to do the
- * substitution into. If not, make it larger (with a bit
- * extra to avoid too many calls to alloc()/free()).
- */
- len = (unsigned)STRLEN(new_start);
- needed_len += len;
- if (needed_len > new_start_len)
- {
- new_start_len = needed_len + 50;
- if ((p1 = alloc_check(new_start_len)) == NULL)
- {
- vim_free(new_start);
- goto outofmem;
- }
- mch_memmove(p1, new_start, (size_t)(len + 1));
- vim_free(new_start);
- new_start = p1;
- }
- new_end = new_start + len;
- }
-
- /*
- * copy the text up to the part that matched
- */
- mch_memmove(new_end, sub_firstline + copycol, (size_t)i);
- new_end += i;
-
- (void)vim_regsub_multi(®match, sub_firstlnum,
- sub, new_end, TRUE, p_magic, TRUE);
- sub_nsubs++;
- did_sub = TRUE;
-
- /* For a multi-line match, make a copy of the last matched
- * line and continue in that one. */
- if (nmatch > 1)
- {
- sub_firstlnum += nmatch - 1;
- vim_free(sub_firstline);
- sub_firstline = vim_strsave(ml_get(sub_firstlnum));
- /* When going beyond the last line, stop substituting. */
- if (sub_firstlnum <= line2)
- do_again = TRUE;
- else
- do_all = FALSE;
- }
-
- /* Remember next character to be copied. */
- copycol = regmatch.endpos[0].col;
-
- /*
- * Now the trick is to replace CTRL-M chars with a real line
- * break. This would make it impossible to insert a CTRL-M in
- * the text. The line break can be avoided by preceding the
- * CTRL-M with a backslash. To be able to insert a backslash,
- * they must be doubled in the string and are halved here.
- * That is Vi compatible.
- */
- for (p1 = new_end; *p1; ++p1)
- {
- if (p1[0] == '\\' && p1[1] != NUL)
- STRCPY(p1, p1 + 1); /* remove backslash */
- else if (*p1 == CR)
- {
- if (u_inssub(lnum) == OK) /* prepare for undo */
- {
- *p1 = NUL; /* truncate up to the CR */
- ml_append(lnum - 1, new_start,
- (colnr_T)(p1 - new_start + 1), FALSE);
- mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
- if (do_ask)
- appended_lines(lnum - 1, 1L);
- else
- {
- if (first_line == 0)
- first_line = lnum;
- last_line = lnum + 1;
- }
- /* All line numbers increase. */
- ++sub_firstlnum;
- ++lnum;
- ++line2;
- /* move the cursor to the new line, like Vi */
- ++curwin->w_cursor.lnum;
- STRCPY(new_start, p1 + 1); /* copy the rest */
- p1 = new_start - 1;
- }
- }
- #ifdef FEAT_MBYTE
- else if (has_mbyte)
- p1 += (*mb_ptr2len_check)(p1) - 1;
- #endif
- }
-
- /*
- * 4. If do_all is set, find next match.
- * Prevent endless loop with patterns that match empty
- * strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
- */
- skip:
- nmatch = -1;
- lastone = (sub_firstline[matchcol] == NUL
- || got_int || got_quit || !(do_all || do_again));
- if (lastone
- || do_ask
- || (nmatch = vim_regexec_multi(®match, curwin,
- curbuf, sub_firstlnum, matchcol)) == 0)
- {
- if (new_start != NULL)
- {
- /*
- * Copy the rest of the line, that didn't match.
- * matchcol has to be adjusted, we use the end of the
- * line as reference, because the substitute may have
- * changed the number of characters.
- */
- STRCAT(new_start, sub_firstline + copycol);
- matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
-
- if (u_savesub(lnum) != OK)
- break;
- ml_replace(lnum, new_start, TRUE);
-
- if (nmatch_tl > 0)
- {
- /*
- * Matched lines have now been substituted and are
- * useless, delete them. The part after the match
- * has been appended to new_start, we don't need
- * it in the buffer.
- */
- ++lnum;
- if (u_savedel(lnum, nmatch_tl) != OK)
- break;
- for (i = 0; i < nmatch_tl; ++i)
- ml_delete(lnum, (int)FALSE);
- mark_adjust(lnum, lnum + nmatch_tl - 1,
- (long)MAXLNUM, -nmatch_tl);
- if (do_ask)
- deleted_lines(lnum, nmatch_tl);
- --lnum;
- line2 -= nmatch_tl; /* nr of lines decreases */
- nmatch_tl = 0;
- }
-
- /* When asking, undo is saved each time, must also set
- * changed flag each time. */
- if (do_ask)
- changed_bytes(lnum, 0);
- else
- {
- if (first_line == 0)
- first_line = lnum;
- last_line = lnum + 1;
- }
-
- sub_firstlnum = lnum;
- vim_free(sub_firstline); /* free the temp buffer */
- sub_firstline = new_start;
- new_start = NULL;
- matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
- copycol = 0;
- }
- if (nmatch == -1 && !lastone)
- nmatch = vim_regexec_multi(®match, curwin, curbuf,
- sub_firstlnum, matchcol);
-
- /*
- * 5. break if there isn't another match in this line
- */
- if (nmatch <= 0)
- break;
- }
-
- line_breakcheck();
- }
-
- if (did_sub)
- ++sub_nlines;
- vim_free(sub_firstline); /* free the copy of the original line */
- sub_firstline = NULL;
- }
-
- line_breakcheck();
- }
-
- if (first_line != 0)
- {
- /* Need to subtract the number of added lines from "last_line" to get
- * the line number before the change (same as adding the number of
- * deleted lines). */
- i = curbuf->b_ml.ml_line_count - old_line_count;
- changed_lines(first_line, 0, last_line - i, i);
- }
-
- outofmem:
- vim_free(sub_firstline); /* may have to free allocated copy of the line */
- if (sub_nsubs)
- {
- /* Set the '[ and '] marks. */
- curbuf->b_op_start.lnum = eap->line1;
- curbuf->b_op_end.lnum = line2;
- curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
-
- if (!global_busy)
- {
- beginline(BL_WHITE | BL_FIX);
- if (!do_sub_msg() && do_ask)
- MSG("");
- }
- else
- global_need_beginline = TRUE;
- if (do_print)
- print_line(curwin->w_cursor.lnum, FALSE);
- }
- else if (!global_busy)
- {
- if (got_int) /* interrupted */
- EMSG(_(e_interr));
- else if (got_match) /* did find something but nothing substituted */
- MSG("");
- else if (do_error) /* nothing found */
- EMSG2(_(e_patnotf2), get_search_pat());
- }
-
- vim_free(regmatch.regprog);
- }
-
- /*
- * Give message for number of substitutions.
- * Can also be used after a ":global" command.
- * Return TRUE if a message was given.
- */
- static int
- do_sub_msg()
- {
- /*
- * Only report substitutions when:
- * - more than 'report' substitutions
- * - command was typed by user, or number of changed lines > 'report'
- * - giving messages is not disabled by 'lazyredraw'
- */
- if (sub_nsubs > p_report
- && (KeyTyped || sub_nlines > 1 || p_report < 1)
- && messaging())
- {
- if (got_int)
- STRCPY(msg_buf, _("(Interrupted) "));
- else
- msg_buf[0] = NUL;
- if (sub_nsubs == 1)
- STRCAT(msg_buf, _("1 substitution"));
- else
- sprintf((char *)msg_buf + STRLEN(msg_buf), _("%ld substitutions"),
- sub_nsubs);
- if (sub_nlines == 1)
- STRCAT(msg_buf, _(" on 1 line"));
- else
- sprintf((char *)msg_buf + STRLEN(msg_buf), _(" on %ld lines"),
- (long)sub_nlines);
- if (msg(msg_buf))
- {
- /* save message to display it after redraw */
- set_keep_msg(msg_buf);
- keep_msg_attr = 0;
- }
- return TRUE;
- }
- if (got_int)
- {
- EMSG(_(e_interr));
- return TRUE;
- }
- return FALSE;
- }
-
- /*
- * Execute a global command of the form:
- *
- * g/pattern/X : execute X on all lines where pattern matches
- * v/pattern/X : execute X on all lines where pattern does not match
- *
- * where 'X' is an EX command
- *
- * The command character (as well as the trailing slash) is optional, and
- * is assumed to be 'p' if missing.
- *
- * This is implemented in two passes: first we scan the file for the pattern and
- * set a mark for each line that (not) matches. secondly we execute the command
- * for each line that has a mark. This is required because after deleting
- * lines we do not know where to search for the next match.
- */
- void
- ex_global(eap)
- exarg_T *eap;
- {
- linenr_T lnum; /* line number according to old situation */
- int ndone;
- int type; /* first char of cmd: 'v' or 'g' */
- char_u *cmd; /* command argument */
-
- char_u delim; /* delimiter, normally '/' */
- char_u *pat;
- regmmatch_T regmatch;
- int match;
- int which_pat;
-
- if (global_busy)
- {
- EMSG(_("E147: Cannot do :global recursive")); /* will increment global_busy */
- return;
- }
-
- if (eap->forceit) /* ":global!" is like ":vglobal" */
- type = 'v';
- else
- type = *eap->cmd;
- cmd = eap->arg;
- which_pat = RE_LAST; /* default: use last used regexp */
- sub_nsubs = 0;
- sub_nlines = 0;
-
- /*
- * undocumented vi feature:
- * "\/" and "\?": use previous search pattern.
- * "\&": use previous substitute pattern.
- */
- if (*cmd == '\\')
- {
- ++cmd;
- if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
- {
- EMSG(_(e_backslash));
- return;
- }
- if (*cmd == '&')
- which_pat = RE_SUBST; /* use previous substitute pattern */
- else
- which_pat = RE_SEARCH; /* use previous search pattern */
- ++cmd;
- pat = (char_u *)"";
- }
- else if (*cmd == NUL)
- {
- EMSG(_("E148: Regular expression missing from global"));
- return;
- }
- else
- {
- delim = *cmd; /* get the delimiter */
- if (delim)
- ++cmd; /* skip delimiter if there is one */
- pat = cmd; /* remember start of pattern */
- cmd = skip_regexp(cmd, delim, p_magic);
- if (cmd[0] == delim) /* end delimiter found */
- *cmd++ = NUL; /* replace it with a NUL */
- }
-
- #ifdef FEAT_FKMAP /* when in Farsi mode, reverse the character flow */
- if (p_altkeymap && curwin->w_p_rl)
- lrFswap(pat,0);
- #endif
-
- if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, ®match) == FAIL)
- {
- EMSG(_(e_invcmd));
- return;
- }
-
- /*
- * pass 1: set marks for each (not) matching line
- */
- ndone = 0;
- for (lnum = eap->line1; lnum <= eap->line2 && !got_int; ++lnum)
- {
- /* a match on this line? */
- match = vim_regexec_multi(®match, curwin, curbuf, lnum, (colnr_T)0);
- if ((type == 'g' && match) || (type == 'v' && !match))
- {
- ml_setmarked(lnum);
- ndone++;
- }
- line_breakcheck();
- }
-
- /*
- * pass 2: execute the command for each line that has been marked
- */
- if (got_int)
- MSG(_(e_interr));
- else if (ndone == 0)
- {
- if (type == 'v')
- smsg((char_u *)_("Pattern found in every line: %s"), pat);
- else
- smsg((char_u *)_(e_patnotf2), pat);
- }
- else
- global_exe(cmd);
-
- ml_clearmarked(); /* clear rest of the marks */
- vim_free(regmatch.regprog);
- }
-
- /*
- * Execute "cmd" on lines marked with ml_setmarked().
- */
- void
- global_exe(cmd)
- char_u *cmd;
- {
- linenr_T old_lcount; /* b_ml.ml_line_count before the command */
- linenr_T lnum; /* line number according to old situation */
-
- /*
- * Set current position only once for a global command.
- * If global_busy is set, setpcmark() will not do anything.
- * If there is an error, global_busy will be incremented.
- */
- setpcmark();
-
- /* When the command writes a message, don't overwrite the command. */
- msg_didout = TRUE;
-
- global_need_beginline = FALSE;
- global_busy = 1;
- old_lcount = curbuf->b_ml.ml_line_count;
- while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1)
- {
- curwin->w_cursor.lnum = lnum;
- curwin->w_cursor.col = 0;
- if (*cmd == NUL || *cmd == '\n')
- do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT);
- else
- do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT);
- ui_breakcheck();
- }
-
- global_busy = 0;
- if (global_need_beginline)
- beginline(BL_WHITE | BL_FIX);
- else
- check_cursor(); /* cursor may be beyond the end of the line */
-
- /* If it looks like no message was written, allow overwriting the
- * command with the report for number of changes. */
- if (msg_col == 0 && msg_scrolled == 0)
- msg_didout = FALSE;
-
- /* If subsitutes done, report number of substitues, otherwise report
- * number of extra or deleted lines. */
- if (!do_sub_msg())
- msgmore(curbuf->b_ml.ml_line_count - old_lcount);
- }
-
- #ifdef FEAT_VIMINFO
- int
- read_viminfo_sub_string(virp, force)
- vir_T *virp;
- int force;
- {
- if (old_sub != NULL && force)
- vim_free(old_sub);
- if (force || old_sub == NULL)
- old_sub = viminfo_readstring(virp, 1, TRUE);
- return viminfo_readline(virp);
- }
-
- void
- write_viminfo_sub_string(fp)
- FILE *fp;
- {
- if (get_viminfo_parameter('/') != 0 && old_sub != NULL)
- {
- fprintf(fp, _("\n# Last Substitute String:\n$"));
- viminfo_writestring(fp, old_sub);
- }
- }
- #endif /* FEAT_VIMINFO */
-
- #if (defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)) || defined(PROTO)
- /*
- * Set up for a tagpreview.
- */
- void
- prepare_tagpreview()
- {
- win_T *wp;
-
- #ifdef FEAT_GUI
- need_mouse_correct = TRUE;
- #endif
-
- /*
- * If there is already a preview window open, use that one.
- */
- if (!curwin->w_p_pvw)
- {
- for (wp = firstwin; wp != NULL; wp = wp->w_next)
- if (wp->w_p_pvw)
- break;
- if (wp != NULL)
- win_enter(wp, TRUE);
- else
- {
- /*
- * There is no preview window open yet. Create one.
- */
- if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
- == FAIL)
- return;
- curwin->w_p_pvw = TRUE;
- }
- }
- }
-
- #endif
-
-
- /*
- * ":help": open a read-only window on a help file
- */
- void
- ex_help(eap)
- exarg_T *eap;
- {
- char_u *arg;
- FILE *helpfd; /* file descriptor of help file */
- int n;
- #ifdef FEAT_WINDOWS
- win_T *wp;
- #endif
- int num_matches;
- char_u **matches;
- int need_free = FALSE;
- char_u *p;
-
- if (eap != NULL)
- {
- /*
- * A ":help" command ends at the first LF, or at a '|' that is
- * followed by some text. Set nextcmd to the following command.
- */
- for (arg = eap->arg; *arg; ++arg)
- {
- if (*arg == '\n' || *arg == '\r' || (*arg == '|' && arg[1] != NUL))
- {
- *arg++ = NUL;
- eap->nextcmd = arg;
- break;
- }
- }
- arg = eap->arg;
-
- if (eap->skip) /* not executing commands */
- return;
- }
- else
- arg = (char_u *)"";
-
- /*
- * If an argument is given, check if there is a match for it.
- */
- if (*arg != NUL)
- {
- /* remove trailing blanks */
- p = arg + STRLEN(arg) - 1;
- while (p > arg && vim_iswhite(*p) && p[-1] != '\\')
- *p-- = NUL;
-
- n = find_help_tags(arg, &num_matches, &matches);
- if (num_matches == 0 || n == FAIL)
- {
- EMSG2(_("E149: Sorry, no help for %s"), arg);
- return;
- }
-
- /* The first match is the best match. */
- arg = vim_strsave(matches[0]);
- need_free = TRUE;
- FreeWild(num_matches, matches);
- }
-
- #ifdef FEAT_GUI
- need_mouse_correct = TRUE;
- #endif
-
- /*
- * Re-use an existing help window or open a new one.
- */
- if (!curwin->w_buffer->b_help)
- {
- #ifdef FEAT_WINDOWS
- for (wp = firstwin; wp != NULL; wp = wp->w_next)
- if (wp->w_buffer != NULL && wp->w_buffer->b_help)
- break;
- if (wp != NULL && wp->w_buffer->b_nwindows > 0)
- win_enter(wp, TRUE);
- else
- #endif
- {
- /*
- * There is no help buffer yet.
- * Try to open the file specified by the "helpfile" option.
- */
- if ((helpfd = mch_fopen((char *)p_hf, READBIN)) == NULL)
- {
- smsg((char_u *)_("Sorry, help file \"%s\" not found"), p_hf);
- goto erret;
- }
- fclose(helpfd);
-
- #ifdef FEAT_WINDOWS
- /* Split off help window; put it at far top if no position
- * specified, the current window is vertically split and narrow. */
- n = WSP_HELP;
- # ifdef FEAT_VERTSPLIT
- if (cmdmod.split == 0 && curwin->w_width != Columns
- && curwin->w_width < 80)
- n |= WSP_TOP;
- # endif
- if (win_split(0, n) == FAIL)
- #else
- /* use current window */
- if (!can_abandon(curbuf, FALSE))
- #endif
- goto erret;
-
- #ifdef FEAT_WINDOWS
- if (curwin->w_height < p_hh)
- win_setheight((int)p_hh);
- #endif
-
- /*
- * open help file (do_ecmd() will set b_help flag, readfile() will
- * set b_p_ro flag)
- */
- (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
- ECMD_HIDE + ECMD_SET_HELP);
- }
- }
-
- if (!p_im)
- restart_edit = 0; /* don't want insert mode in help file */
-
- if (arg == NULL || *arg == NUL)
- {
- arg = (char_u *)"help.txt"; /* go to the index */
- need_free = FALSE;
- }
- do_tag(arg, DT_HELP, 1, FALSE, TRUE);
-
- /*
- * Always set these options after jumping to a help tag, because the user
- * may have an autocommand that gets in the way.
- * accept all chars for keywords, except ' ', '*', '"', '|'.
- * Only set it when needed, buf_init_chartab() is some work.
- */
- p =
- #ifdef EBCDIC
- (char_u *)"65-255,^*,^|,^\"";
- #else
- (char_u *)"!-~,^*,^|,^\"";
- #endif
- if (STRCMP(curbuf->b_p_isk, p) != 0)
- {
- set_string_option_direct((char_u *)"isk", -1, p, OPT_FREE|OPT_LOCAL);
- check_buf_options(curbuf);
- (void)buf_init_chartab(curbuf, FALSE);
- }
-
- curbuf->b_p_ts = 8;
- curwin->w_p_list = FALSE;
-
- erret:
- if (need_free)
- vim_free(arg);
- }
-
-
- /*
- * Return a heuristic indicating how well the given string matches. The
- * smaller the number, the better the match. This is the order of priorities,
- * from best match to worst match:
- * - Match with least alpha-numeric characters is better.
- * - Match with least total characters is better.
- * - Match towards the start is better.
- * - Match starting with "+" is worse (feature instead of command)
- * Assumption is made that the matched_string passed has already been found to
- * match some string for which help is requested. webb.
- */
- int
- help_heuristic(matched_string, offset, wrong_case)
- char_u *matched_string;
- int offset; /* offset for match */
- int wrong_case; /* no matching case */
- {
- int num_letters;
- char_u *p;
-
- num_letters = 0;
- for (p = matched_string; *p; p++)
- if (ASCII_ISALNUM(*p))
- num_letters++;
-
- /*
- * Multiply the number of letters by 100 to give it a much bigger
- * weighting than the number of characters.
- * If there only is a match while ignoring case, add 5000.
- * If the match starts in the middle of a word, add 10000 to put it
- * somewhere in the last half.
- * If the match is more than 2 chars from the start, multiply by 200 to
- * put it after matches at the start.
- */
- if (ASCII_ISALNUM(matched_string[offset]) && offset > 0
- && ASCII_ISALNUM(matched_string[offset - 1]))
- offset += 10000;
- else if (offset > 2)
- offset *= 200;
- if (wrong_case)
- offset += 5000;
- /* Features are less interesting than the subjects themselves, but "+"
- * alone is not a feature. */
- if (matched_string[0] == '+' && matched_string[1] != NUL)
- offset += 100;
- return (int)(100 * num_letters + STRLEN(matched_string) + offset);
- }
-
- /*
- * Compare functions for qsort() below, that checks the help heuristics number
- * that has been put after the tagname by find_tags().
- */
- static int
- #ifdef __BORLANDC__
- _RTLENTRYF
- #endif
- help_compare(s1, s2)
- const void *s1;
- const void *s2;
- {
- char *p1;
- char *p2;
-
- p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
- p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
- return strcmp(p1, p2);
- }
-
- /*
- * Find all help tags matching "arg", sort them and return in matches[], with
- * the number of matches in num_matches.
- * The matches will be sorted with a "best" match algorithm.
- */
- int
- find_help_tags(arg, num_matches, matches)
- char_u *arg;
- int *num_matches;
- char_u ***matches;
- {
- char_u *s, *d;
- int i;
- static char *(mtable[]) = {"*", "g*", "[*", "]*", ":*",
- "/*", "/\\*", "\"*", "/\\(\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??",
- "/\\?",
- "[count]", "[quotex]", "[range]",
- "[pattern]", "\\|", "\\%$"};
- static char *(rtable[]) = {"star", "gstar", "[star", "]star", ":star",
- "/star", "/\\\\star", "quotestar", "/\\\\(\\\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??",
- "/\\\\?",
- "\\[count]", "\\[quotex]", "\\[range]",
- "\\[pattern]", "\\\\bar", "/\\\\%\\$"};
-
- d = IObuff; /* assume IObuff is long enough! */
-
- /*
- * Recognize a few exceptions to the rule. Some strings that contain '*'
- * with "star". Otherwise '*' is recognized as a wildcard.
- */
- for (i = sizeof(mtable) / sizeof(char *); --i >= 0; )
- if (STRCMP(arg, mtable[i]) == 0)
- {
- STRCPY(d, rtable[i]);
- break;
- }
-
- if (i < 0) /* no match in table */
- {
- /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
- * Also replace "\%^" and "\%(", they match every tag too.
- * And also "\_$" and "\_^". */
-
- if (arg[0] == '\\'
- && ((arg[1] != NUL && arg[2] == NUL)
- || ((arg[1] == '%' || arg[1] == '_')
- && arg[2] != NUL && arg[3] == NUL)))
- {
- STRCPY(d, "/\\\\");
- STRCPY(d + 3, arg + 1);
- /* Check for "/\\_$", should be "/\\_\$" */
- if (d[3] == '_' && d[4] == '$')
- STRCPY(d + 4, "\\$");
- }
- else
- {
- /* replace "[:...:]" with "\[:...:]"; "[+...]" with "\[++...]" */
- if (arg[0] == '[' && (arg[1] == ':'
- || (arg[1] == '+' && arg[2] == '+')))
- *d++ = '\\';
-
- for (s = arg; *s; ++s)
- {
- /*
- * Replace "|" with "bar" and '"' with "quote" to match the name of
- * the tags for these commands.
- * Replace "*" with ".*" and "?" with "." to match command line
- * completion.
- * Insert a backslash before '~', '$' and '.' to avoid their
- * special meaning.
- */
- if (d - IObuff > IOSIZE - 10) /* getting too long!? */
- break;
- switch (*s)
- {
- case '|': STRCPY(d, "bar");
- d += 3;
- continue;
- case '"': STRCPY(d, "quote");
- d += 5;
- continue;
- case '*': *d++ = '.';
- break;
- case '?': *d++ = '.';
- continue;
- case '$':
- case '.':
- case '~': *d++ = '\\';
- break;
- }
-
- /*
- * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
- * ":help i_^_CTRL-D" work.
- * Insert '-' before and after "CTRL-X" when applicable.
- */
- if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1])
- || vim_strchr((char_u *)"?@[\\]^", s[1]) != NULL)))
- {
- if (d > IObuff && d[-1] != '_')
- *d++ = '_'; /* prepend a '_' */
- STRCPY(d, "CTRL-");
- d += 5;
- if (*s < ' ')
- #ifdef EBCDIC
- *d++ = CtrlChar(*s);
- #else
- *d++ = *s + '@';
- #endif
- else
- *d++ = *++s;
- if (s[1] != NUL && s[1] != '_')
- *d++ = '_'; /* append a '_' */
- continue;
- }
- else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
- *d++ = '\\';
-
- /*
- * Insert a backslash before a backslash after a slash, for search
- * pattern tags: "/\|" --> "/\\|".
- */
- else if (s[0] == '\\' && s[1] != '\\'
- && *arg == '/' && s == arg + 1)
- *d++ = '\\';
-
- *d++ = *s;
-
- /*
- * If tag starts with ', toss everything after a second '. Fixes
- * CTRL-] on 'option'. (would include the trailing '.').
- */
- if (*s == '\'' && s > arg && *arg == '\'')
- break;
- }
- *d = NUL;
- }
- }
-
- *matches = (char_u **)"";
- *num_matches = 0;
- if (find_tags(IObuff, num_matches, matches,
- TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE, (int)MAXCOL) == OK)
- /*
- * Sort the matches found on the heuristic number that is after the
- * tag name.
- */
- qsort((void *)*matches, (size_t)*num_matches,
- sizeof(char_u *), help_compare)
- ;
- return OK;
- }
-
- /*
- * After reading a help file: May cleanup a help buffer when syntax
- * highlighting is not used.
- */
- void
- fix_help_buffer()
- {
- linenr_T lnum;
- char_u *line;
- int in_example = FALSE;
- int len;
- char_u *p;
- char_u *rt;
- int mustfree;
-
- /* set filetype to "help". */
- set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL);
-
- #ifdef FEAT_SYN_HL
- if (!syntax_present(curbuf))
- #endif
- {
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
- {
- line = ml_get_buf(curbuf, lnum, FALSE);
- len = (int)STRLEN(line);
- if (in_example && len > 0 && !vim_iswhite(line[0]))
- {
- /* End of example: non-white or '<' in first column. */
- if (line[0] == '<')
- {
- /* blank-out a '<' in the first column */
- line = ml_get_buf(curbuf, lnum, TRUE);
- line[0] = ' ';
- }
- in_example = FALSE;
- }
- if (!in_example && len > 0)
- {
- if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' '))
- {
- /* blank-out a '>' in the last column (start of example) */
- line = ml_get_buf(curbuf, lnum, TRUE);
- line[len - 1] = ' ';
- in_example = TRUE;
- }
- else if (line[len - 1] == '~')
- {
- /* blank-out a '~' at the end of line (header marker) */
- line = ml_get_buf(curbuf, lnum, TRUE);
- line[len - 1] = ' ';
- }
- }
- }
- }
-
- /*
- * In the "help.txt" file, add the locally added help files.
- * This uses the very first line in the help file.
- */
- if (fnamecmp(gettail(curbuf->b_fname), "help.txt") == 0)
- {
- for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
- {
- line = ml_get_buf(curbuf, lnum, FALSE);
- if (STRNCMP(line, "LOCAL ADDITIONS", 15) == 0)
- {
- /* Go through all directories in 'runtimepath', skipping
- * $VIMRUNTIME. */
- p = p_rtp;
- while (*p != NUL)
- {
- copy_option_part(&p, NameBuff, MAXPATHL, ",");
- mustfree = FALSE;
- rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
- if (fnamecmp(NameBuff, rt) != 0)
- {
- int fcount;
- char_u **fnames;
- FILE *fd;
- char_u *s;
- int fi;
-
- /* Find all "doc/ *.txt" files in this directory. */
- add_pathsep(NameBuff);
- STRCAT(NameBuff, "doc/*.txt");
- if (gen_expand_wildcards(1, &NameBuff, &fcount,
- &fnames, EW_FILE|EW_SILENT) == OK
- && fcount > 0)
- {
- for (fi = 0; fi < fcount; ++fi)
- {
- fd = fopen((char *)fnames[fi], "r");
- if (fd != NULL)
- {
- vim_fgets(IObuff, IOSIZE, fd);
- if (IObuff[0] == '*'
- && (s = vim_strchr(IObuff + 1, '*'))
- != NULL)
- {
- /* Change tag definition to a
- * reference and remove <CR>/<NL>. */
- IObuff[0] = '|';
- *s = '|';
- while (*s != NUL)
- {
- if (*s == '\r' || *s == '\n')
- *s = NUL;
- ++s;
- }
- ml_append(lnum, IObuff, (colnr_T)0,
- FALSE);
- ++lnum;
- }
- fclose(fd);
- }
- }
- FreeWild(fcount, fnames);
- }
- }
- if (mustfree)
- vim_free(rt);
- }
- break;
- }
- }
- }
- }
-
- #if defined(FEAT_EX_EXTRA) || defined(PROTO)
- /*
- * ":helptags"
- */
- void
- ex_helptags(eap)
- exarg_T *eap;
- {
- FILE *fd_tags;
- FILE *fd;
- garray_T ga;
- int filecount;
- char_u **files;
- char_u *p1, *p2;
- int fi;
- char_u *s;
- int i;
- char_u *fname;
-
- if (!mch_isdir(eap->arg))
- {
- EMSG2(_("E150: Not a directory: %s"), eap->arg);
- return;
- }
-
- /*
- * Find all *.txt files.
- */
- STRCPY(NameBuff, eap->arg);
- add_pathsep(NameBuff);
- STRCAT(NameBuff, "*.txt");
- if (gen_expand_wildcards(1, &NameBuff, &filecount, &files,
- EW_FILE|EW_SILENT) == FAIL
- || filecount == 0)
- {
- EMSG2("E151: No match: %s", NameBuff);
- return;
- }
-
- /*
- * Open the tags file for writing.
- * Do this before scanning through all the files.
- */
- STRCPY(NameBuff, eap->arg);
- add_pathsep(NameBuff);
- STRCAT(NameBuff, "tags");
- fd_tags = fopen((char *)NameBuff, "w");
- if (fd_tags == NULL)
- {
- EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
- FreeWild(filecount, files);
- return;
- }
-
- /*
- * Go over all the files and extract the tags.
- */
- ga_init2(&ga, (int)sizeof(char_u *), 100);
- for (fi = 0; fi < filecount && !got_int; ++fi)
- {
- fd = fopen((char *)files[fi], "r");
- if (fd == NULL)
- {
- EMSG2(_("E153: Unable to open %s for reading"), files[fi]);
- continue;
- }
- fname = gettail(files[fi]);
-
- while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int)
- {
- p1 = vim_strchr(IObuff, '*'); /* find first '*' */
- while (p1 != NULL)
- {
- p2 = vim_strchr(p1 + 1, '*'); /* find second '*' */
- if (p2 != NULL && p2 > p1 + 1) /* skip "*" and "**" */
- {
- for (s = p1 + 1; s < p2; ++s)
- if (*s == ' ' || *s == '\t' || *s == '|')
- break;
-
- /*
- * Only accept a *tag* when it consists of valid
- * characters, there is no '-' before it and is followed
- * by a white character or end-of-line.
- */
- if (s == p2
- && (p1 == IObuff || p1[-1] != '-')
- && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL
- || s[1] == '\0'))
- {
- *p2 = '\0';
- ++p1;
- if (ga_grow(&ga, 1) == FAIL)
- {
- got_int = TRUE;
- break;
- }
- s = alloc((unsigned)(p2 - p1 + STRLEN(fname) + 2));
- if (s == NULL)
- {
- got_int = TRUE;
- break;
- }
- ((char_u **)ga.ga_data)[ga.ga_len] = s;
- ++ga.ga_len;
- --ga.ga_room;
- sprintf((char *)s, "%s\t%s", p1, fname);
-
- /* find next '*' */
- p2 = vim_strchr(p2 + 1, '*');
- }
- }
- p1 = p2;
- }
- line_breakcheck();
- }
-
- fclose(fd);
- }
-
- FreeWild(filecount, files);
-
- if (!got_int)
- {
- /*
- * Sort the tags.
- */
- sort_strings((char_u **)ga.ga_data, ga.ga_len);
-
- /*
- * Check for duplicates.
- */
- for (i = 1; i < ga.ga_len; ++i)
- {
- p1 = ((char_u **)ga.ga_data)[i - 1];
- p2 = ((char_u **)ga.ga_data)[i];
- while (*p1 == *p2)
- {
- if (*p2 == '\t')
- {
- *p2 = NUL;
- sprintf((char *)NameBuff,
- _("E154: Duplicate tag \"%s\" in file %s"),
- ((char_u **)ga.ga_data)[i], p2 + 1);
- EMSG(NameBuff);
- *p2 = '\t';
- break;
- }
- ++p1;
- ++p2;
- }
- }
-
- /*
- * Write the tags into the file.
- */
- for (i = 0; i < ga.ga_len; ++i)
- {
- s = ((char_u **)ga.ga_data)[i];
- fprintf(fd_tags, "%s\t/*", s);
- for (p1 = s; *p1 != '\t'; ++p1)
- {
- /* insert backslash before '\\' and '/' */
- if (*p1 == '\\' || *p1 == '/')
- putc('\\', fd_tags);
- putc(*p1, fd_tags);
- }
- fprintf(fd_tags, "*\n");
- }
- }
-
- for (i = 0; i < ga.ga_len; ++i)
- vim_free(((char_u **)ga.ga_data)[i]);
- ga_clear(&ga);
- fclose(fd_tags); /* there is no check for an error... */
- }
- #endif
-
- #if defined(FEAT_SIGNS) || defined(PROTO)
-
- /*
- * Struct to hold the sign properties.
- */
- typedef struct sign sign_T;
-
- struct sign
- {
- sign_T *sn_next; /* next sign in list */
- int sn_typenr; /* type number of sign (negative if not equal
- to name) */
- char_u *sn_name; /* name of sign */
- char_u *sn_icon; /* name of pixmap */
- #ifdef FEAT_SIGN_ICONS
- XImage *sn_image; /* icon image */
- #endif
- char_u *sn_text; /* text used instead of pixmap */
- int sn_line_hl; /* highlight ID for line */
- int sn_text_hl; /* highlight ID for text */
- };
-
- #define MAX_TYPENR 255 /* depends on sattr_T */
- static sign_T *first_sign = NULL;
- static int last_sign_typenr = MAX_TYPENR; /* is decremented */
-
- static void sign_list_defined __ARGS((sign_T *sp));
-
- /*
- * ":sign" command
- */
- void
- ex_sign(eap)
- exarg_T *eap;
- {
- char_u *arg = eap->arg;
- char_u *p;
- int idx;
- sign_T *sp;
- sign_T *sp_prev;
- buf_T *buf;
- static char *cmds[] = {
- "define",
- #define SIGNCMD_DEFINE 0
- "undefine",
- #define SIGNCMD_UNDEFINE 1
- "list",
- #define SIGNCMD_LIST 2
- "place",
- #define SIGNCMD_PLACE 3
- "unplace",
- #define SIGNCMD_UNPLACE 4
- "jump",
- #define SIGNCMD_JUMP 5
- #define SIGNCMD_LAST 6
- };
-
- /* Parse the subcommand. */
- p = skiptowhite(arg);
- if (*p != NUL)
- *p++ = NUL;
- for (idx = 0; ; ++idx)
- {
- if (idx == SIGNCMD_LAST)
- {
- EMSG2(_("E160: Unknown sign command: %s"), arg);
- return;
- }
- if (STRCMP(arg, cmds[idx]) == 0)
- break;
- }
- arg = skipwhite(p);
-
- if (idx <= SIGNCMD_LIST)
- {
- /*
- * Define, undefine or list signs.
- */
- if (idx == SIGNCMD_LIST && *arg == NUL)
- {
- /* ":sign list": list all defined signs */
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- sign_list_defined(sp);
- }
- else if (*arg == NUL)
- EMSG(_("E156: Missing sign name"));
- else
- {
- p = skiptowhite(arg);
- if (*p != NUL)
- *p++ = NUL;
- sp_prev = NULL;
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- {
- if (STRCMP(sp->sn_name, arg) == 0)
- break;
- sp_prev = sp;
- }
- if (idx == SIGNCMD_DEFINE)
- {
- /* ":sign define {name} ...": define a sign */
- if (sp == NULL)
- {
- /* Allocate a new sign. */
- sp = (sign_T *)alloc_clear((unsigned)sizeof(sign_T));
- if (sp == NULL)
- return;
- if (sp_prev == NULL)
- first_sign = sp;
- else
- sp_prev->sn_next = sp;
- sp->sn_name = vim_strnsave(arg, (int)(p - arg));
-
- /* If the name is a number use that for the typenr,
- * otherwise use a negative number. */
- if (isdigit(*arg))
- sp->sn_typenr = atoi((char *)arg);
- else
- {
- sign_T *lp;
- int start = last_sign_typenr;
-
- for (lp = first_sign; lp != NULL; lp = lp->sn_next)
- {
- if (lp->sn_typenr == last_sign_typenr)
- {
- --last_sign_typenr;
- if (last_sign_typenr == 0)
- last_sign_typenr = MAX_TYPENR;
- if (last_sign_typenr == start)
- {
- EMSG(_("E255: Too many signs defined"));
- return;
- }
- lp = first_sign;
- continue;
- }
- }
-
- sp->sn_typenr = last_sign_typenr--;
- if (last_sign_typenr == 0)
- last_sign_typenr = MAX_TYPENR; /* wrap around */
- }
- }
-
- /* set values for a defined sign. */
- for (;;)
- {
- arg = skipwhite(p);
- if (*arg == NUL)
- break;
- p = skiptowhite(arg);
- if (STRNCMP(arg, "icon=", 5) == 0)
- {
- arg += 5;
- vim_free(sp->sn_icon);
- sp->sn_icon = vim_strnsave(arg, (int)(p - arg));
- #ifdef FEAT_SIGN_ICONS
- if (gui.in_use)
- {
- if (sp->sn_image != NULL)
- gui_mch_destroy_sign(sp->sn_image);
- sp->sn_image = gui_mch_register_sign(sp->sn_icon);
- }
- #endif
- }
- else if (STRNCMP(arg, "text=", 5) == 0)
- {
- arg += 5;
- /* Currently must have two printable characters. */
- if (!vim_isprintc(arg[0]) || !vim_isprintc(arg[1])
- || p - arg != 2)
- {
- EMSG2(_("E239: Invalid sign text: %s"), arg);
- return;
- }
- vim_free(sp->sn_text);
- sp->sn_text = vim_strnsave(arg, (int)(p - arg));
- }
- else if (STRNCMP(arg, "linehl=", 7) == 0)
- {
- arg += 7;
- sp->sn_line_hl = syn_check_group(arg, (int)(p - arg));
- }
- else if (STRNCMP(arg, "texthl=", 7) == 0)
- {
- arg += 7;
- sp->sn_text_hl = syn_check_group(arg, (int)(p - arg));
- }
- else
- {
- EMSG2(_(e_invarg2), arg);
- return;
- }
- }
- }
- else if (sp == NULL)
- EMSG2(_("E155: Unknown sign: %s"), arg);
- else if (idx == SIGNCMD_LIST)
- /* ":sign list {name}" */
- sign_list_defined(sp);
- else
- {
- /* ":sign undefine {name}" */
- vim_free(sp->sn_name);
- vim_free(sp->sn_icon);
- #ifdef FEAT_SIGN_ICONS
- if (sp->sn_image != NULL)
- gui_mch_destroy_sign(sp->sn_image);
- #endif
- vim_free(sp->sn_text);
- if (sp_prev == NULL)
- first_sign = sp->sn_next;
- else
- sp_prev->sn_next = sp->sn_next;
- vim_free(sp);
- }
- }
- }
- else
- {
- int id = -1;
- linenr_T lnum = -1;
- char_u *sign_name = NULL;
- char_u *arg1;
-
- if (*arg == NUL)
- {
- if (idx == SIGNCMD_PLACE)
- {
- /* ":sign place": list placed signs in all buffers */
- sign_list_placed(NULL);
- }
- else if (idx == SIGNCMD_UNPLACE)
- {
- /* ":sign unplace": remove placed sign at cursor */
- id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum);
- if (id > 0)
- {
- buf_delsign(curwin->w_buffer, id);
- update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum);
- }
- else
- EMSG(_("E159: Missing sign number"));
- }
- else
- EMSG(_(e_argreq));
- return;
- }
-
- if (idx == SIGNCMD_UNPLACE && arg[0] == '*' && arg[1] == NUL)
- {
- /* ":sign unplace *": remove all placed signs */
- buf_delete_all_signs();
- return;
- }
-
- /* first arg could be placed sign id */
- arg1 = arg;
- if (isdigit(*arg))
- {
- id = getdigits(&arg);
- if (!vim_iswhite(*arg) && *arg != NUL)
- {
- id = -1;
- arg = arg1;
- }
- else
- {
- arg = skipwhite(arg);
- if (idx == SIGNCMD_UNPLACE && *arg == NUL)
- {
- /* ":sign unplace {id}": remove placed sign by number */
- for (buf = firstbuf; buf != NULL; buf = buf->b_next)
- if ((lnum = buf_delsign(buf, id)) != 0)
- update_debug_sign(buf, lnum);
- return;
- }
- }
- }
-
- /*
- * Check for line={lnum} name={name} and file={fname} or buffer={nr}.
- * Leave "arg" pointing to {fname}.
- */
- for (;;)
- {
- if (STRNCMP(arg, "line=", 5) == 0)
- {
- arg += 5;
- lnum = atoi((char *)arg);
- arg = skiptowhite(arg);
- }
- else if (STRNCMP(arg, "name=", 5) == 0)
- {
- arg += 5;
- sign_name = arg;
- arg = skiptowhite(arg);
- if (*arg != NUL)
- *arg++ = NUL;
- }
- else if (STRNCMP(arg, "file=", 5) == 0)
- {
- arg += 5;
- buf = buflist_findname(arg);
- break;
- }
- else if (STRNCMP(arg, "buffer=", 7) == 0)
- {
- arg += 7;
- buf = buflist_findnr(atoi((char *)arg));
- break;
- }
- else
- {
- EMSG(_(e_invarg));
- return;
- }
- arg = skipwhite(arg);
- }
-
- if (buf == NULL)
- {
- EMSG2(_("E158: Invalid buffer name: %s"), arg);
- }
- else if (id <= 0)
- {
- if (lnum >= 0 || sign_name != NULL)
- EMSG(_(e_invarg));
- else
- /* ":sign place file={fname}": list placed signs in one file */
- sign_list_placed(buf);
- }
- else if (idx == SIGNCMD_JUMP)
- {
- /* ":sign jump {id} file={fname}" */
- if (lnum >= 0 || sign_name != NULL)
- EMSG(_(e_invarg));
- else if ((lnum = buf_findsign(buf, id)) > 0)
- { /* goto a sign ... */
- if (buf_jump_open_win(buf) != NULL)
- { /* ... in a current window */
- curwin->w_cursor.lnum = lnum;
- check_cursor_lnum();
- beginline(BL_WHITE);
- }
- else
- { /* ... not currently in a window */
- char_u *cmd;
-
- cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25);
- if (cmd == NULL)
- return;
- sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
- do_cmdline_cmd(cmd);
- vim_free(cmd);
- }
- #ifdef FEAT_FOLDING
- foldOpenCursor();
- #endif
- }
- else
- EMSGN(_("E157: Invalid sign ID: %ld"), id);
- }
- else if (idx == SIGNCMD_UNPLACE)
- {
- /* ":sign unplace {id} file={fname}" */
- if (lnum >= 0 || sign_name != NULL)
- EMSG(_(e_invarg));
- else
- {
- lnum = buf_delsign(buf, id);
- update_debug_sign(buf, lnum);
- }
- }
- /* idx == SIGNCMD_PLACE */
- else if (sign_name != NULL)
- {
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- if (STRCMP(sp->sn_name, sign_name) == 0)
- break;
- if (sp == NULL)
- {
- EMSG2(_("E155: Unknown sign: %s"), sign_name);
- return;
- }
- if (lnum > 0)
- /* ":sign place {id} line={lnum} name={name} file={fname}":
- * place a sign */
- buf_addsign(buf, id, lnum, sp->sn_typenr);
- else
- /* ":sign place {id} file={fname}": change sign type */
- lnum = buf_change_sign_type(buf, id, sp->sn_typenr);
- update_debug_sign(buf, lnum);
- }
- else
- EMSG(_(e_invarg));
- }
- }
-
- #if defined(FEAT_SIGN_ICONS) || defined(PROTO)
- /*
- * Allocate the icons. Called when the GUI has started. Allows defining
- * signs before it starts.
- */
- void
- sign_gui_started()
- {
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- if (sp->sn_icon != NULL)
- sp->sn_image = gui_mch_register_sign(sp->sn_icon);
- }
- #endif
-
- /*
- * List one sign.
- */
- static void
- sign_list_defined(sp)
- sign_T *sp;
- {
- char_u *p;
-
- smsg((char_u *)"sign %s", sp->sn_name);
- if (sp->sn_icon != NULL)
- {
- MSG_PUTS(" icon=");
- msg_outtrans(sp->sn_icon);
- #ifdef FEAT_SIGN_ICONS
- if (sp->sn_image == NULL)
- MSG_PUTS(" (NOT FOUND)");
- #else
- MSG_PUTS(" (not supported)");
- #endif
- }
- if (sp->sn_text != NULL)
- {
- MSG_PUTS(" text=");
- msg_outtrans(sp->sn_text);
- }
- if (sp->sn_line_hl > 0)
- {
- MSG_PUTS(" linehl=");
- p = get_highlight_name(NULL, sp->sn_line_hl - 1);
- if (p == NULL)
- MSG_PUTS("NONE");
- else
- msg_puts(p);
- }
- if (sp->sn_text_hl > 0)
- {
- MSG_PUTS(" texthl=");
- p = get_highlight_name(NULL, sp->sn_text_hl - 1);
- if (p == NULL)
- MSG_PUTS("NONE");
- else
- msg_puts(p);
- }
- }
-
- /*
- * Get highlighting attribute for sign "typenr".
- * If "line" is TRUE: line highl, if FALSE: text highl.
- */
- int
- sign_get_attr(typenr, line)
- int typenr;
- int line;
- {
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- if (sp->sn_typenr == typenr)
- {
- if (line)
- {
- if (sp->sn_line_hl > 0)
- return syn_id2attr(sp->sn_line_hl);
- }
- else
- {
- if (sp->sn_text_hl > 0)
- return syn_id2attr(sp->sn_text_hl);
- }
- break;
- }
- return 0;
- }
-
- /*
- * Get text mark for sign "typenr".
- * Returns NULL if there isn't one.
- */
- char_u *
- sign_get_text(typenr)
- int typenr;
- {
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- if (sp->sn_typenr == typenr)
- return sp->sn_text;
- return NULL;
- }
-
- #if defined(FEAT_SIGN_ICONS) || defined(PROTO)
- void *
- sign_get_image(typenr)
- int typenr; /* the attribute which may have a sign */
- {
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- if (sp->sn_typenr == typenr)
- return (void *)sp->sn_image;
- return NULL;
- }
- #endif
-
- /*
- * Get the name of a sign by its typenr.
- */
- char_u *
- sign_typenr2name(typenr)
- int typenr;
- {
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
- if (sp->sn_typenr == typenr)
- return sp->sn_name;
- return (char_u *)_("[Deleted]");
- }
-
- #endif
-
- #if defined(FEAT_GUI) || defined(FEAT_CLIENTSERVER) || defined(PROTO)
- /*
- * ":drop"
- */
- void
- ex_drop(eap)
- exarg_T *eap;
- {
- int split = FALSE;
-
- /* Check whether the current buffer is changed. If so, we will need
- * to split the current window or data could be lost.
- * We don't need to check if the 'hidden' option is set, as in this
- * case the buffer won't be lost.
- */
- if (!P_HID(curbuf))
- {
- ++emsg_off;
- split = check_changed(curbuf, TRUE, FALSE, FALSE, FALSE);
- --emsg_off;
- }
-
- /* Fake a ":snext" or ":next" command. */
- if (split)
- {
- eap->cmdidx = CMD_snext;
- eap->cmd[0] = 's';
- }
- else
- eap->cmdidx = CMD_next;
- ex_next(eap);
- }
- #endif
-